Skip to content

Ellar 0.7.0 support #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions docs/migrations/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ from logging.config import fileConfig

from alembic import context
from ellar.app import current_injector
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async

from ellar_sql.migrations import SingleDatabaseAlembicEnvMigration
from ellar_sql.services import EllarSQLService
Expand All @@ -28,7 +28,7 @@ fileConfig(config.config_file_name) # type:ignore[arg-type]
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


@run_as_async
async def main() -> None:
db_service: EllarSQLService = current_injector.get(EllarSQLService)

Expand All @@ -41,7 +41,7 @@ async def main() -> None:
await alembic_env_migration.run_migrations_online(context) # type:ignore[arg-type]


execute_coroutine_with_sync_worker(main())
main()
```

The EllarSQL migration package provides two main migration classes:
Expand Down Expand Up @@ -92,7 +92,7 @@ from alembic import context
from ellar_sql.migrations import AlembicEnvMigrationBase
from ellar_sql.model.database_binds import get_metadata
from ellar.app import current_injector
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async
from ellar_sql.services import EllarSQLService

# This is the Alembic Config object, which provides
Expand Down Expand Up @@ -155,6 +155,8 @@ class MyCustomMigrationEnv(AlembicEnvMigrationBase):
with context.begin_transaction():
context.run_migrations()


@run_as_async
async def main() -> None:
db_service: EllarSQLService = current_injector.get(EllarSQLService)

Expand All @@ -166,7 +168,7 @@ async def main() -> None:
else:
await alembic_env_migration.run_migrations_online(context)

execute_coroutine_with_sync_worker(main())
main()
```

This migration environment class, `MyCustomMigrationEnv`, inherits from `AlembicEnvMigrationBase`
Expand Down
10 changes: 4 additions & 6 deletions ellar_sql/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
"""EllarSQL Module adds support for SQLAlchemy and Alembic package to your Ellar application"""

__version__ = "0.0.1"
__version__ = "0.0.2"

from .model.database_binds import get_all_metadata, get_metadata
from .module import EllarSQLModule
from .pagination import LimitOffsetPagination, PageNumberPagination, paginate
from .query import (
first_or_404,
get_or_404,
one_or_404,
)
from .query import first_or_404, first_or_none, get_or_404, get_or_none, one_or_404
from .schemas import MigrationOption, ModelBaseConfig, SQLAlchemyConfig
from .services import EllarSQLService

Expand All @@ -21,6 +17,8 @@
"get_or_404",
"first_or_404",
"one_or_404",
"first_or_none",
"get_or_none",
"paginate",
"PageNumberPagination",
"LimitOffsetPagination",
Expand Down
7 changes: 4 additions & 3 deletions ellar_sql/factory/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import sqlalchemy as sa
import sqlalchemy.orm as sa_orm
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async
from factory.alchemy import (
SESSION_PERSISTENCE_COMMIT,
SESSION_PERSISTENCE_FLUSH,
Expand Down Expand Up @@ -36,12 +36,13 @@ class Meta:
abstract = True

@classmethod
def _session_execute(
@run_as_async
async def _session_execute(
cls, session_func: t.Callable, *args: t.Any, **kwargs: t.Any
) -> t.Union[sa.Result, sa.CursorResult, t.Any]:
res = session_func(*args, **kwargs)
if isinstance(res, t.Coroutine):
res = execute_coroutine_with_sync_worker(res)
res = await res
return res

@classmethod
Expand Down
9 changes: 0 additions & 9 deletions ellar_sql/model/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import sqlalchemy as sa
import sqlalchemy.orm as sa_orm
from pydantic.v1 import BaseModel

from ellar_sql.constant import ABSTRACT_KEY, DATABASE_KEY, DEFAULT_KEY, TABLE_KEY
from ellar_sql.model.utils import (
Expand All @@ -12,13 +11,6 @@
)
from ellar_sql.schemas import ModelBaseConfig, ModelMetaStore


class asss(BaseModel):
sd: str


IncEx = t.Union[t.Set[int], t.Set[str], t.Dict[int, t.Any], t.Dict[str, t.Any]]

if t.TYPE_CHECKING:
from .base import ModelBase

Expand Down Expand Up @@ -143,7 +135,6 @@ def dict(
exclude: t.Optional[t.Set[str]] = None,
exclude_none: bool = False,
) -> t.Dict[str, t.Any]:
# TODO: implement advance exclude and include that goes deep into relationships too
return dict(
self._iter(include=include, exclude_none=exclude_none, exclude=exclude)
)
3 changes: 3 additions & 0 deletions ellar_sql/model/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ class Table(sa.Table):
"""
Custom SQLAlchemy Table class that supports database-binding
E.g.:
```python
from ellar_sql.model import Table

user_book_m2m = Table(
"user_book",
sa.Column("user_id", sa.ForeignKey(User.id), primary_key=True),
sa.Column("book_id", sa.ForeignKey(Book.id), primary_key=True),
__database__='default'
)
```
"""

@t.overload
Expand Down
6 changes: 3 additions & 3 deletions ellar_sql/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

import sqlalchemy as sa
from ellar.common import IExecutionContext, IModuleSetup, Module, middleware
from ellar.common.utils.importer import get_main_directory_by_stack
from ellar.core import Config, DynamicModule, ModuleBase, ModuleSetup
from ellar.di import ProviderConfig, request_or_transient_scope
from ellar.events import app_context_teardown_events
from ellar.events import app_context_teardown
from ellar.utils.importer import get_main_directory_by_stack
from sqlalchemy.ext.asyncio import (
AsyncEngine,
AsyncSession,
Expand Down Expand Up @@ -155,7 +155,7 @@ def __setup_module(cls, sql_alchemy_config: SQLAlchemyConfig) -> DynamicModule:
)

providers.append(ProviderConfig(EllarSQLService, use_value=db_service))
app_context_teardown_events.connect(
app_context_teardown.connect(
functools.partial(cls._on_application_tear_down, db_service=db_service)
)

Expand Down
16 changes: 12 additions & 4 deletions ellar_sql/pagination/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sqlalchemy as sa
import sqlalchemy.orm as sa_orm
from ellar.app import current_injector
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async
from sqlalchemy.ext.asyncio import AsyncSession

from ellar_sql.model.base import ModelBase
Expand Down Expand Up @@ -275,7 +275,13 @@ def __init__(
)

if self._created_session:
self._session.close() # session usage is done but only if Paginator created the session
self._close_session() # session usage is done but only if Paginator created the session

@run_as_async
async def _close_session(self) -> None:
res = self._session.close()
if isinstance(res, t.Coroutine):
await res

def _get_session(self) -> t.Union[sa_orm.Session, AsyncSession, t.Any]:
self._created_session = True
Expand All @@ -284,14 +290,15 @@ def _get_session(self) -> t.Union[sa_orm.Session, AsyncSession, t.Any]:

def _query_items(self) -> t.List[t.Any]:
if self._is_async:
res = execute_coroutine_with_sync_worker(self._query_items_async())
res = self._query_items_async()
return list(res)
return self._query_items_sync()

def _query_items_sync(self) -> t.List[t.Any]:
select = self._select.limit(self.per_page).offset(self._query_offset)
return list(self._session.execute(select).unique().scalars())

@run_as_async
async def _query_items_async(self) -> t.List[t.Any]:
session = t.cast(AsyncSession, self._session)

Expand All @@ -302,7 +309,7 @@ async def _query_items_async(self) -> t.List[t.Any]:

def _query_count(self) -> int:
if self._is_async:
res = execute_coroutine_with_sync_worker(self._query_count_async())
res = self._query_count_async()
return int(res)
return self._query_count_sync()

Expand All @@ -313,6 +320,7 @@ def _query_count_sync(self) -> int:
).scalar()
return out # type:ignore[return-value]

@run_as_async
async def _query_count_async(self) -> int:
session = t.cast(AsyncSession, self._session)

Expand Down
8 changes: 3 additions & 5 deletions ellar_sql/query/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from .utils import (
first_or_404,
get_or_404,
one_or_404,
)
from .utils import first_or_404, first_or_none, get_or_404, get_or_none, one_or_404

__all__ = [
"get_or_404",
"one_or_404",
"first_or_404",
"first_or_none",
"get_or_none",
]
37 changes: 37 additions & 0 deletions ellar_sql/query/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ async def get_or_404(
return t.cast(_O, value)


async def get_or_none(
entity: t.Type[_O],
ident: t.Any,
**kwargs: t.Any,
) -> t.Optional[_O]:
""" """
db_service = current_injector.get(EllarSQLService)
session = db_service.get_scoped_session()()

value = session.get(entity, ident, **kwargs)

if isinstance(value, t.Coroutine):
value = await value

if value is None:
return None

return t.cast(_O, value)


async def first_or_404(
statement: sa.sql.Select[t.Any], *, error_message: t.Optional[str] = None
) -> t.Any:
Expand All @@ -51,6 +71,23 @@ async def first_or_404(
return value


async def first_or_none(statement: sa.sql.Select[t.Any]) -> t.Any:
""" """
db_service = current_injector.get(EllarSQLService)
session = db_service.session_factory()

result = session.execute(statement)
if isinstance(result, t.Coroutine):
result = await result

value = result.scalar()

if value is None:
return None

return value


async def one_or_404(
statement: sa.sql.Select[t.Any], *, error_message: t.Optional[str] = None
) -> t.Any:
Expand Down
10 changes: 5 additions & 5 deletions ellar_sql/services/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
import sqlalchemy.exc as sa_exc
import sqlalchemy.orm as sa_orm
from ellar.common.exceptions import ImproperConfiguration
from ellar.common.utils.importer import (
from ellar.threading.sync_worker import execute_coroutine
from ellar.utils.importer import (
get_main_directory_by_stack,
module_import,
)
from ellar.threading import execute_coroutine_with_sync_worker
from sqlalchemy.ext.asyncio import (
AsyncSession,
async_scoped_session,
Expand Down Expand Up @@ -151,7 +151,7 @@ def create_all(self, *databases: str) -> None:

for metadata_engine in metadata_engines:
if metadata_engine.is_async():
execute_coroutine_with_sync_worker(metadata_engine.create_all_async())
execute_coroutine(metadata_engine.create_all_async())
continue
metadata_engine.create_all()

Expand All @@ -162,7 +162,7 @@ def drop_all(self, *databases: str) -> None:

for metadata_engine in metadata_engines:
if metadata_engine.is_async():
execute_coroutine_with_sync_worker(metadata_engine.drop_all_async())
execute_coroutine(metadata_engine.drop_all_async())
continue
metadata_engine.drop_all()

Expand All @@ -173,7 +173,7 @@ def reflect(self, *databases: str) -> None:

for metadata_engine in metadata_engines:
if metadata_engine.is_async():
execute_coroutine_with_sync_worker(metadata_engine.reflect_async())
execute_coroutine(metadata_engine.reflect_async())
continue
metadata_engine.reflect()

Expand Down
5 changes: 3 additions & 2 deletions ellar_sql/templates/multiple/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from alembic import context
from ellar.app import current_injector
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async

from ellar_sql.migrations import MultipleDatabaseAlembicEnvMigration
from ellar_sql.services import EllarSQLService
Expand All @@ -22,6 +22,7 @@
# ... etc.


@run_as_async
async def main() -> None:
db_service: EllarSQLService = current_injector.get(EllarSQLService)

Expand All @@ -34,4 +35,4 @@ async def main() -> None:
await alembic_env_migration.run_migrations_online(context) # type:ignore[arg-type]


execute_coroutine_with_sync_worker(main())
main()
5 changes: 3 additions & 2 deletions ellar_sql/templates/single/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from alembic import context
from ellar.app import current_injector
from ellar.threading import execute_coroutine_with_sync_worker
from ellar.threading import run_as_async

from ellar_sql.migrations import SingleDatabaseAlembicEnvMigration
from ellar_sql.services import EllarSQLService
Expand All @@ -22,6 +22,7 @@
# ... etc.


@run_as_async
async def main() -> None:
db_service: EllarSQLService = current_injector.get(EllarSQLService)

Expand All @@ -34,4 +35,4 @@ async def main() -> None:
await alembic_env_migration.run_migrations_online(context) # type:ignore[arg-type]


execute_coroutine_with_sync_worker(main())
main()
1 change: 1 addition & 0 deletions examples/db-learning/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ After environment setup, kindly follow instruction below
```
python manage.py runserver --reload
```
Visit Swagger Docs: [http://localhost:8000/docs](http://localhost:8000/docs)


## Run Test
Expand Down
Loading