Skip to content

Commit

Permalink
feature: MongoDB support via beanie (#187)
Browse files Browse the repository at this point in the history
Co-authored-by: Pavel Kirilin <[email protected]>
Co-authored-by: Tsung-Ju Lii <[email protected]>
  • Loading branch information
3 people authored Dec 6, 2023
1 parent d801ad3 commit 8d73952
Show file tree
Hide file tree
Showing 25 changed files with 488 additions and 24 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ docker run --rm -it -v "$(pwd):/projects" s3rius/fastapi_template
One of the coolest features is that this project is extremely configurable.
You can choose between different databases and even ORMs, or
you can even generate a project without a database!
Currently SQLAlchemy 2.0, TortoiseORM, Piccolo and Ormar are supported.
Currently SQLAlchemy 2.0, TortoiseORM, Piccolo, Ormar and Beanie are supported.

This project can run as TUI or CLI and has excellent code documentation.

Expand Down Expand Up @@ -82,11 +82,11 @@ Options:
-n, --name TEXT Name of your awesome project
-V, --version Prints current version
--force Owerrite directory if it exists
--quite Do not ask for features during generation
--quiet Do not ask for features during generation
--api-type [rest|graphql] Select API type for your application
--db [none|sqlite|mysql|postgresql]
--db [none|sqlite|mysql|postgresql|mongodb]
Select a database for your app
--orm [none|ormar|sqlalchemy|tortoise|psycopg|piccolo]
--orm [none|ormar|sqlalchemy|tortoise|psycopg|piccolo|beanie]
Choose Object–Relational Mapper lib
--ci [none|gitlab_ci|github] Select a CI for your app
--redis Add redis support
Expand Down
42 changes: 37 additions & 5 deletions fastapi_template/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def disable_orm(ctx: BuilderContext) -> Optional[MenuEntry]:
return None


def do_not_ask_features_if_quite(ctx: BuilderContext) -> Optional[List[MenuEntry]]:
if ctx.quite:
def do_not_ask_features_if_quiet(ctx: BuilderContext) -> Optional[List[MenuEntry]]:
if ctx.quiet:
return [SKIP_ENTRY]
return None

Expand Down Expand Up @@ -185,6 +185,23 @@ def checker(ctx: BuilderContext) -> bool:
port=5432,
),
),
MenuEntry(
code="mongodb",
user_view="MongoDB",
description=(
"{name} is one of the most popular NoSQL databases out there.".format(
name=colored("MongoDB", color="green"),
)
),
additional_info=Database(
name="mongodb",
image="mongo:7.0",
async_driver="beanie",
driver_short="mongodb",
driver="mongodb",
port=27017
),
)
],
)

Expand Down Expand Up @@ -243,7 +260,7 @@ def checker(ctx: BuilderContext) -> bool:
entries=[
MenuEntry(
code="none",
user_view="Whithout ORMs",
user_view="Without ORMs",
description=(
"If you select this option, you will get only {what}.\n"
"The rest {warn}.".format(
Expand All @@ -256,6 +273,7 @@ def checker(ctx: BuilderContext) -> bool:
MenuEntry(
code="ormar",
user_view="Ormar",
is_hidden=check_db(["sqlite", "mysql", "postgresql"]),
pydantic_v1=True,
description=(
"{what} is a great {feature} ORM.\n"
Expand All @@ -268,6 +286,7 @@ def checker(ctx: BuilderContext) -> bool:
MenuEntry(
code="sqlalchemy",
user_view="SQLAlchemy",
is_hidden=check_db(["sqlite", "mysql", "postgresql"]),
description=(
"{what} is the most popular python ORM.\n"
"It has a {feature} and a big community around it.".format(
Expand All @@ -279,6 +298,7 @@ def checker(ctx: BuilderContext) -> bool:
MenuEntry(
code="tortoise",
user_view="Tortoise",
is_hidden=check_db(["sqlite", "mysql", "postgresql"]),
description=(
"{what} is a great {feature} ORM.\n"
"It's easy to use, it has it's own migration tooling.".format(
Expand Down Expand Up @@ -312,6 +332,18 @@ def checker(ctx: BuilderContext) -> bool:
)
),
),
MenuEntry(
code="beanie",
user_view="Beanie",
is_hidden=check_db(["mongodb"]),
description=(
"{what} is an asynchronous object-document mapper (ODM) for MongoDB.\n"
"Data models are based on Pydantic.".format(
what=colored("Beanie", color="green"),
)
),
),

],
)

Expand All @@ -320,7 +352,7 @@ def checker(ctx: BuilderContext) -> bool:
code="features",
description="Additional project features",
multiselect=True,
before_ask=do_not_ask_features_if_quite,
before_ask=do_not_ask_features_if_quiet,
entries=[
MenuEntry(
code="pydanticv1",
Expand Down Expand Up @@ -661,7 +693,7 @@ def run_command(callback: Callable[[BuilderContext], None]) -> None:
help="Owerrite directory if it exists",
),
Option(
["--quite"],
["--quiet"],
is_flag=True,
help="Do not ask for features during generation",
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ jobs:
MYSQL_DATABASE: "{{ cookiecutter.project_name }}"
MYSQL_AUTHENTICATION_PLUGIN: "mysql_native_password"
{%- endif %}
{%- if cookiecutter.db_info.name == "mysql" %}
{%- if cookiecutter.db_info.name == "mongodb" %}
MONGO_INITDB_ROOT_USERNAME: "{{ cookiecutter.project_name }}"
MONGO_INITDB_ROOT_PASSWORD: "{{ cookiecutter.project_name }}"
{%- endif %}
{%- if cookiecutter.db_info.name == "mysql" %}
options: >-
--health-cmd="mysqladmin ping -u root"
--health-interval=15s
Expand Down Expand Up @@ -148,6 +152,9 @@ jobs:
{%- if cookiecutter.db_info.name != "sqlite" %}
{{ cookiecutter.project_name | upper }}_DB_HOST: localhost
{%- endif %}
{%- if cookiecutter.db_info.name == "mongodb" %}
{{ cookiecutter.project_name | upper }}_DB_BASE: admin
{%- endif %}
{%- endif %}
{%- if cookiecutter.enable_rmq == "True" %}
{{ cookiecutter.project_name | upper }}_RABBIT_HOST: localhost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ pytest:
MYSQL_DATABASE: {{ cookiecutter.project_name }}
ALLOW_EMPTY_PASSWORD: yes
{%- endif %}

{%- if cookiecutter.db_info.name == "mongodb" %}

# MongoDB variables
{{ cookiecutter.project_name | upper }}_DB_HOST: database
{{ cookiecutter.project_name | upper }}_DB_BASE: admin
MONGO_INITDB_ROOT_USERNAME: {{ cookiecutter.project_name }}
MONGO_INITDB_ROOT_PASSWORD: {{ cookiecutter.project_name }}
{%- endif %}

{%- if cookiecutter.enable_rmq == "True" %}

# Rabbitmq variables
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ By default it runs:
You can read more about pre-commit here: https://pre-commit.com/
{%- if cookiecutter.enable_kube == 'True' %}
## Kubernetes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,18 @@
"alembic.ini",
"{{cookiecutter.project_name}}/web/api/dummy",
"{{cookiecutter.project_name}}/web/gql/dummy",
"{{cookiecutter.project_name}}/db_sa",
"{{cookiecutter.project_name}}/tests/test_dummy.py",
"deploy/kube/db.yml"
]
},
"Beanie support": {
"enabled": "{{cookiecutter.db_info.name == 'mongodb'}}",
"resources": [
"{{cookiecutter.project_name}}/db_beanie"
]
},
"Postgres and MySQL support": {
"enabled": "{{cookiecutter.db_info.name != 'sqlite'}}",
"enabled": "{{cookiecutter.db_info.name not in ['sqlite', 'mongodb']}}",
"resources": [
"deploy/kube/db.yml"
]
Expand Down Expand Up @@ -143,6 +148,7 @@
"{{cookiecutter.project_name}}/tests/test_dummy.py",
"{{cookiecutter.project_name}}/db_piccolo/dao",
"{{cookiecutter.project_name}}/db_piccolo/models/dummy_model.py",
"{{cookiecutter.project_name}}/db_beanie/models/dummy_model.py",
"{{cookiecutter.project_name}}/db_sa/migrations/versions/2021-08-16-16-55_2b7380507a71.py",
"{{cookiecutter.project_name}}/db_ormar/migrations/versions/2021-08-16-16-55_2b7380507a71.py",
"{{cookiecutter.project_name}}/db_tortoise/migrations/models/1_20210928165300_init_dummy_pg.sql",
Expand Down Expand Up @@ -189,6 +195,12 @@
"{{cookiecutter.project_name}}/piccolo_conf.py"
]
},
"Beanie": {
"enabled": "{{cookiecutter.orm == 'beanie'}}",
"resources": [
"{{cookiecutter.project_name}}/db_beanie"
]
},
"Postgresql DB": {
"enabled": "{{cookiecutter.db_info.name == 'postgresql'}}",
"resources": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
# Exposes application port.
- "8000:8000"
build:
context: .
target: dev
volumes:
# Adds current directory as volume.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ services:
{{cookiecutter.project_name | upper}}_DB_PORT: {{cookiecutter.db_info.port}}
{{cookiecutter.project_name | upper}}_DB_USER: {{cookiecutter.project_name}}
{{cookiecutter.project_name | upper}}_DB_PASS: {{cookiecutter.project_name}}
{%- if cookiecutter.db_info.name == "mongodb" %}
{{cookiecutter.project_name | upper}}_DB_BASE: admin
{%- else %}
{{cookiecutter.project_name | upper}}_DB_BASE: {{cookiecutter.project_name}}
{%- endif %}
{%- endif %}
{%- endif %}
{%- if cookiecutter.enable_rmq == 'True' %}
{{cookiecutter.project_name | upper }}_RABBIT_HOST: {{cookiecutter.project_name}}-rmq
{%- endif %}
Expand Down Expand Up @@ -103,6 +107,24 @@ services:
retries: 40
{%- endif %}

{%- if cookiecutter.db_info.name == "mongodb"%}
db:
image: {{cookiecutter.db_info.image}}
hostname: {{cookiecutter.project_name}}-db
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: "{{cookiecutter.project_name}}"
MONGO_INITDB_ROOT_PASSWORD: "{{cookiecutter.project_name}}"
command: "mongod"
volumes:
- {{cookiecutter.project_name}}-db-data:/data/db
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
interval: 10s
timeout: 5s
retries: 40
{%- endif %}

{%- if cookiecutter.db_info.name == "mysql" %}

db:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ aiofiles = "^23.1.0"
psycopg = { version = "^3.1.9", extras = ["binary", "pool"] }
{%- endif %}
httptools = "^0.6.0"
{%- if cookiecutter.orm == "beanie" %}
beanie = "^1.21.0"
{%- else %}
pymongo = "^4.5.0"
{%- endif %}
{%- if cookiecutter.api_type == "graphql" %}
strawberry-graphql = { version = "^0.194.4", extras = ["fastapi"] }
{%- endif %}
Expand Down Expand Up @@ -220,6 +225,8 @@ env = [
"{{cookiecutter.project_name | upper}}_ENVIRONMENT=pytest",
{%- if cookiecutter.db_info.name == "sqlite" %}
"{{cookiecutter.project_name | upper}}_DB_FILE=test_db.sqlite3",
{%- elif cookiecutter.db_info.name == "mongodb" %}
"{{cookiecutter.project_name | upper}}_DB_BASE=admin",
{%- else %}
"{{cookiecutter.project_name | upper}}_DB_BASE={{cookiecutter.project_name}}_test",
{%- endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"{{cookiecutter.project_name}}/db_ormar",
"{{cookiecutter.project_name}}/db_tortoise",
"{{cookiecutter.project_name}}/db_psycopg",
"{{cookiecutter.project_name}}/db_piccolo"
"{{cookiecutter.project_name}}/db_piccolo",
"{{cookiecutter.project_name}}/db_beanie"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
from piccolo.conf.apps import Finder
from piccolo.table import create_tables, drop_tables

{%- elif cookiecutter.orm == "beanie" %}
import beanie
from motor.motor_asyncio import AsyncIOMotorClient

{%- endif %}


Expand Down Expand Up @@ -255,7 +259,7 @@ async def create_tables(connection: AsyncConnection[Any]) -> None:


@pytest.fixture
async def dbpool() -> AsyncGenerator[AsyncConnectionPool, None]:
async def dbpool() -> AsyncGenerator[AsyncConnectionPool[Any], None]:
"""
Creates database connections pool to test database.
Expand Down Expand Up @@ -332,6 +336,23 @@ async def setup_db() -> AsyncGenerator[None, None]:
await drop_database(engine)
{%- endif %}

{%- elif cookiecutter.orm == "beanie" %}
@pytest.fixture(autouse=True)
async def setup_db() -> AsyncGenerator[None, None]:
"""
Fixture to create database connection.
:yield: nothing.
"""
client = AsyncIOMotorClient(settings.db_url.human_repr()) # type: ignore
from {{cookiecutter.project_name}}.db.models import load_all_models # noqa: WPS433
await beanie.init_beanie(
database=client[settings.db_base],
document_models=load_all_models(),
)
yield


{%- endif %}

{%- if cookiecutter.enable_rmq == 'True' %}
Expand Down Expand Up @@ -456,7 +477,7 @@ def fastapi_app(
{%- if cookiecutter.orm == "sqlalchemy" %}
dbsession: AsyncSession,
{%- elif cookiecutter.orm == "psycopg" %}
dbpool: AsyncConnectionPool,
dbpool: AsyncConnectionPool[Any],
{%- endif %}
{% if cookiecutter.enable_redis == "True" -%}
fake_redis_pool: ConnectionPool,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""DAO classes."""
Loading

0 comments on commit 8d73952

Please sign in to comment.