Skip to content

Commit 2ac5264

Browse files
authored
Merge pull request #10 from python-ellar/docs_jan_18
Documentation and Code refactor
2 parents 1d90469 + ff04943 commit 2ac5264

File tree

109 files changed

+4112
-1425
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+4112
-1425
lines changed

Makefile

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ clean: ## Removing cached python compiled files
99
find . -name \*pyo | xargs rm -fv
1010
find . -name \*~ | xargs rm -fv
1111
find . -name __pycache__ | xargs rm -rfv
12+
find . -name .pytest_cache | xargs rm -rfv
1213
find . -name .ruff_cache | xargs rm -rfv
1314

1415
install: ## Install dependencies
@@ -23,14 +24,14 @@ lint:fmt ## Run code linters
2324
mypy ellar_sql
2425

2526
fmt format:clean ## Run code formatters
26-
ruff format ellar_sql tests
27-
ruff check --fix ellar_sql tests
27+
ruff format ellar_sql tests examples
28+
ruff check --fix ellar_sql tests examples
2829

29-
test: ## Run tests
30-
pytest tests
30+
test:clean ## Run tests
31+
pytest
3132

32-
test-cov: ## Run tests with coverage
33-
pytest --cov=ellar_sql --cov-report term-missing tests
33+
test-cov:clean ## Run tests with coverage
34+
pytest --cov=ellar_sql --cov-report term-missing
3435

3536
pre-commit-lint: ## Runs Requires commands during pre-commit
3637
make clean

README.md

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,30 @@
22
<a href="#" target="blank"><img src="https://python-ellar.github.io/ellar/img/EllarLogoB.png" width="200" alt="Ellar Logo" /></a>
33
</p>
44

5-
![Test](https://github.com/python-ellar/ellar-sqlachemy/actions/workflows/test_full.yml/badge.svg)
6-
![Coverage](https://img.shields.io/codecov/c/github/python-ellar/ellar-sqlalchemy)
7-
[![PyPI version](https://badge.fury.io/py/ellar-sqlachemy.svg)](https://badge.fury.io/py/ellar-sqlachemy)
8-
[![PyPI version](https://img.shields.io/pypi/v/ellar-sqlachemy.svg)](https://pypi.python.org/pypi/ellar-sqlachemy)
9-
[![PyPI version](https://img.shields.io/pypi/pyversions/ellar-sqlachemy.svg)](https://pypi.python.org/pypi/ellar-sqlachemy)
10-
11-
## Project Status
12-
13-
- [ ] Overall Completion - 85% done
14-
- [ ] Tests - 90% Complete
15-
- [x] Model class that transforms to SQLAlchemy Models or DeclarativeBase based on configuration
16-
- [x] Pydantic-like way to exporting model to dictionary object eg:`model.dict(exclude={'x', 'y', 'z'})`
17-
- [x] Support multiple database useful in models and queries
18-
- [x] Session management during request scope and outside request
19-
- [x] Service to manage SQLAlchemy Engine and Session creation, and Migration for async and sync Engines and Sessions
20-
- [x] Async first approach to database migration using Alembic
21-
- [x] Expose all Alembic commands to Ellar CLI
22-
- [x] Module to config and setup SQLAlchemy dependencies and migration path
23-
- [x] SQLAlchemy Pagination for both Templating and API routes
24-
- [x] File and Image SQLAlchemy Columns integration with ellar storage
25-
- [ ] SQLAlchemy Django Like Query
26-
- [ ] Documentation
5+
![Test](https://github.com/python-ellar/ellar-sql/actions/workflows/test_full.yml/badge.svg)
6+
![Coverage](https://img.shields.io/codecov/c/github/python-ellar/ellar-sql)
7+
[![PyPI version](https://badge.fury.io/py/ellar-sql.svg)](https://badge.fury.io/py/ellar-sql)
8+
[![PyPI version](https://img.shields.io/pypi/v/ellar-sql.svg)](https://pypi.python.org/pypi/ellar-sql)
9+
[![PyPI version](https://img.shields.io/pypi/pyversions/ellar-sql.svg)](https://pypi.python.org/pypi/ellar-sql)
10+
2711

2812
## Introduction
29-
Ellar SQLAlchemy Module simplifies the integration of SQLAlchemy and Alembic migration tooling into your ellar application.
13+
EllarSQL Module adds support for `SQLAlchemy` and `Alembic` package to your Ellar application
3014

3115
## Installation
3216
```shell
33-
$(venv) pip install ellar-sqlalchemy
17+
$(venv) pip install ellar-sql
3418
```
3519

20+
This library was inspired by [Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/en/3.1.x/){target="_blank"}
21+
3622
## Features
37-
- Automatic table name
38-
- Session management during request and after request
39-
- Support both async/sync SQLAlchemy operations in Session, Engine, and Connection.
40-
- Multiple Database Support
41-
- Database migrations for both single and multiple databases either async/sync database engine
23+
24+
- Migration
25+
- Single/Multiple Database
26+
- Pagination
27+
- Compatible with SQLAlchemy tools
28+
4229

4330
## **Usage**
4431
In your ellar application, create a module called `db` or any name of your choice,
@@ -56,7 +43,7 @@ from ellar_sql.model import Model
5643

5744

5845
class Base(Model):
59-
__base_config__ = {'make_declarative_base': True}
46+
__base_config__ = {'as_base': True}
6047
__database__ = 'default'
6148

6249
created_date: Mapped[datetime] = mapped_column(
@@ -104,7 +91,7 @@ from .controllers import DbController
10491
},
10592
echo=True,
10693
migration_options={
107-
'directory': '__main__/migrations'
94+
'directory': 'my_migrations_folder'
10895
},
10996
models=['db.models.users']
11097
)

docs/advance/index.md

Whitespace-only changes.

docs/index.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<style>
2+
.md-content .md-typeset h1 { display: none; }
3+
</style>
4+
<p align="center">
5+
<a href="#" target="blank"><img src="https://python-ellar.github.io/ellar/img/EllarLogoB.png" width="200" alt="Ellar Logo" /></a>
6+
</p>
7+
<p align="center">EllarSQL is an SQL database Ellar Module.</p>
8+
9+
![Test](https://github.com/python-ellar/ellar-sql/actions/workflows/test_full.yml/badge.svg)
10+
![Coverage](https://img.shields.io/codecov/c/github/python-ellar/ellar-sql)
11+
[![PyPI version](https://badge.fury.io/py/ellar-sql.svg)](https://badge.fury.io/py/ellar-sql)
12+
[![PyPI version](https://img.shields.io/pypi/v/ellar-sql.svg)](https://pypi.python.org/pypi/ellar-sql)
13+
[![PyPI version](https://img.shields.io/pypi/pyversions/ellar-sql.svg)](https://pypi.python.org/pypi/ellar-sql)
14+
15+
EllarSQL is an SQL database module, leveraging the robust capabilities of [SQLAlchemy](https://www.sqlalchemy.org/) to
16+
seamlessly interact with SQL databases through Python code and objects.
17+
18+
Engineered with precision, EllarSQL is meticulously designed to streamline the integration of **SQLAlchemy** within your
19+
Ellar application. It introduces discerning usage patterns around pivotal objects
20+
such as **model**, **session**, and **engine**, ensuring an efficient and coherent workflow.
21+
22+
Notably, EllarSQL refrains from altering the fundamental workings or usage of SQLAlchemy.
23+
This documentation is focused on the meticulous setup of EllarSQL. For an in-depth exploration of SQLAlchemy,
24+
we recommend referring to the comprehensive [SQLAlchemy documentation](https://docs.sqlalchemy.org/).
25+
26+
## **Feature Highlights**
27+
EllarSQL comes packed with a set of awesome features designed:
28+
29+
- **Migration**: Enjoy an async-first migration solution that seamlessly handles both single and multiple database setups and for both async and sync database engines configuration.
30+
31+
- **Single/Multiple Database**: EllarSQL provides an intuitive setup for models with different databases, allowing you to manage your data across various sources effortlessly.
32+
33+
- **Pagination**: EllarSQL introduces SQLAlchemy Paginator for API/Templated routes, along with support for other fantastic SQLAlchemy pagination tools.
34+
35+
- **Unlimited Compatibility**: EllarSQL plays nice with the entire SQLAlchemy ecosystem. Whether you're using third-party tools or exploring the vast SQLAlchemy landscape, EllarSQL seamlessly integrates with your preferred tooling.
36+
37+
## **Requirements**
38+
EllarSQL core dependencies includes:
39+
40+
- Python >= 3.8
41+
- Ellar >= 0.6.7
42+
- SQLAlchemy >= 2.0.16
43+
- Alembic >= 1.10.0
44+
45+
## **Installation**
46+
47+
```shell
48+
pip install ellar-sql
49+
```
50+
51+
## **Quick Example**
52+
Let's create a simple `User` model.
53+
```python
54+
from ellar_sql import model
55+
56+
57+
class User(model.Model):
58+
id: model.Mapped[int] = model.mapped_column(model.Integer, primary_key=True)
59+
username: model.Mapped[str] = model.mapped_column(model.String, unique=True, nullable=False)
60+
email: model.Mapped[str] = model.mapped_column(model.String)
61+
```
62+
Let's create `app.db` with `User` table in it. For that we need to set up `EllarSQLService` as shown below:
63+
64+
```python
65+
from ellar_sql import EllarSQLService
66+
67+
db_service = EllarSQLService(
68+
databases='sqlite:///app.db',
69+
echo=True,
70+
)
71+
72+
db_service.create_all()
73+
```
74+
If you check your execution directory, you will see `sqlite` directory with `app.db`.
75+
76+
Let's populate our `User` table. To do, we need a session, which is available at `db_service.session_factory`
77+
78+
```python
79+
from ellar_sql import EllarSQLService, model
80+
81+
82+
class User(model.Model):
83+
id: model.Mapped[int] = model.mapped_column(model.Integer, primary_key=True)
84+
username: model.Mapped[str] = model.mapped_column(model.String, unique=True, nullable=False)
85+
email: model.Mapped[str] = model.mapped_column(model.String)
86+
87+
88+
db_service = EllarSQLService(
89+
databases='sqlite:///app.db',
90+
echo=True,
91+
)
92+
93+
db_service.create_all()
94+
95+
session = db_service.session_factory()
96+
97+
for i in range(50):
98+
session.add(User(username=f'username-{i+1}', email=f'user{i+1}doe@example.com'))
99+
100+
101+
session.commit()
102+
rows = session.execute(model.select(User)).scalars()
103+
104+
all_users = [row.dict() for row in rows]
105+
assert len(all_users) == 50
106+
107+
session.close()
108+
```
109+
110+
We have successfully seed `50` users to `User` table in `app.db`.
111+
112+
I know at this point you want to know more, so let's dive deep into the documents and [get started](https://githut.com/python-ellar/ellar-sql/models/).

docs/migrations/env.md

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# **Alembic Env**
2+
3+
In the generated migration template, EllarSQL adopts an async-first approach for handling migration file generation.
4+
This approach simplifies the execution of migrations for both `Session`, `Engine`, `AsyncSession`, and `AsyncEngine`,
5+
but it also introduces a certain level of complexity.
6+
7+
```python
8+
from logging.config import fileConfig
9+
10+
from alembic import context
11+
from ellar.app import current_injector
12+
from ellar.threading import execute_coroutine_with_sync_worker
13+
14+
from ellar_sql.migrations import SingleDatabaseAlembicEnvMigration
15+
from ellar_sql.services import EllarSQLService
16+
17+
# this is the Alembic Config object, which provides
18+
# access to the values within the .ini file in use.
19+
config = context.config
20+
21+
# Interpret the config file for Python logging.
22+
# This line sets up loggers basically.
23+
fileConfig(config.config_file_name) # type:ignore[arg-type]
24+
25+
# logger = logging.getLogger("alembic.env")
26+
# other values from the config, defined by the needs of env.py,
27+
# can be acquired:
28+
# my_important_option = config.get_main_option("my_important_option")
29+
# ... etc.
30+
31+
32+
async def main() -> None:
33+
db_service: EllarSQLService = current_injector.get(EllarSQLService)
34+
35+
# initialize migration class
36+
alembic_env_migration = SingleDatabaseAlembicEnvMigration(db_service)
37+
38+
if context.is_offline_mode():
39+
alembic_env_migration.run_migrations_offline(context) # type:ignore[arg-type]
40+
else:
41+
await alembic_env_migration.run_migrations_online(context) # type:ignore[arg-type]
42+
43+
44+
execute_coroutine_with_sync_worker(main())
45+
```
46+
47+
The EllarSQL migration package provides two main migration classes:
48+
49+
- **SingleDatabaseAlembicEnvMigration**: Manages migrations for a **single** database configuration, catering to both `Engine` and `AsyncEngine`.
50+
- **MultipleDatabaseAlembicEnvMigration**: Manages migrations for **multiple** database configurations, covering both `Engine` and `AsyncEngine`.
51+
52+
## **Customizing the Env file**
53+
54+
To customize or edit the Env file, it is recommended to inherit from either `SingleDatabaseAlembicEnvMigration` or `MultipleDatabaseAlembicEnvMigration` based on your specific configuration. Make the necessary changes within the inherited class.
55+
56+
If you prefer to write something from scratch, then the abstract class `AlembicEnvMigrationBase` is the starting point. This class includes three abstract methods and expects a `EllarSQLService` during initialization, as demonstrated below:
57+
58+
```python
59+
class AlembicEnvMigrationBase:
60+
def __init__(self, db_service: EllarSQLService) -> None:
61+
self.db_service = db_service
62+
self.use_two_phase = db_service.migration_options.use_two_phase
63+
64+
@abstractmethod
65+
def default_process_revision_directives(
66+
self,
67+
context: "MigrationContext",
68+
revision: RevisionArgs,
69+
directives: t.List["MigrationScript"],
70+
) -> t.Any:
71+
pass
72+
73+
@abstractmethod
74+
def run_migrations_offline(self, context: "EnvironmentContext") -> None:
75+
pass
76+
77+
@abstractmethod
78+
async def run_migrations_online(self, context: "EnvironmentContext") -> None:
79+
pass
80+
```
81+
82+
The `run_migrations_online` and `run_migrations_offline` are all similar to the same function from Alembic env.py template.
83+
The `default_process_revision_directives` is a callback is used to prevent an auto-migration from being generated
84+
when there are no changes to the schema described in details [here](http://alembic.zzzcomputing.com/en/latest/cookbook.html)
85+
86+
### Example
87+
```python
88+
import logging
89+
from logging.config import fileConfig
90+
91+
from alembic import context
92+
from ellar_sql.migrations import AlembicEnvMigrationBase
93+
from ellar_sql.model.database_binds import get_metadata
94+
from ellar.app import current_injector
95+
from ellar.threading import execute_coroutine_with_sync_worker
96+
from ellar_sql.services import EllarSQLService
97+
98+
# This is the Alembic Config object, which provides
99+
# access to the values within the .ini file in use.
100+
config = context.config
101+
logger = logging.getLogger("alembic.env")
102+
# Interpret the config file for Python logging.
103+
# This line sets up loggers essentially.
104+
fileConfig(config.config_file_name) # type:ignore[arg-type]
105+
106+
class MyCustomMigrationEnv(AlembicEnvMigrationBase):
107+
def default_process_revision_directives(
108+
self,
109+
context,
110+
revision,
111+
directives,
112+
) -> None:
113+
if getattr(context.config.cmd_opts, "autogenerate", False):
114+
script = directives[0]
115+
if script.upgrade_ops.is_empty():
116+
directives[:] = []
117+
logger.info("No changes in schema detected.")
118+
119+
def run_migrations_offline(self, context: "EnvironmentContext") -> None:
120+
"""Run migrations in 'offline' mode.
121+
122+
This configures the context with just a URL
123+
and not an Engine, though an Engine is acceptable
124+
here as well. By skipping the Engine creation
125+
we don't even need a DBAPI to be available.
126+
127+
Calls to context.execute() here emit the given string to the
128+
script output.
129+
130+
"""
131+
pass
132+
133+
async def run_migrations_online(self, context: "EnvironmentContext") -> None:
134+
"""Run migrations in 'online' mode.
135+
136+
In this scenario, we need to create an Engine
137+
and associate a connection with the context.
138+
139+
"""
140+
key, engine = self.db_service.engines.popitem()
141+
metadata = get_metadata(key, certain=True).metadata
142+
143+
conf_args = {}
144+
conf_args.setdefault(
145+
"process_revision_directives", self.default_process_revision_directives
146+
)
147+
148+
with engine.connect() as connection:
149+
context.configure(
150+
connection=connection,
151+
target_metadata=metadata,
152+
**conf_args
153+
)
154+
155+
with context.begin_transaction():
156+
context.run_migrations()
157+
158+
async def main() -> None:
159+
db_service: EllarSQLService = current_injector.get(EllarSQLService)
160+
161+
# initialize migration class
162+
alembic_env_migration = MyCustomMigrationEnv(db_service)
163+
164+
if context.is_offline_mode():
165+
alembic_env_migration.run_migrations_offline(context)
166+
else:
167+
await alembic_env_migration.run_migrations_online(context)
168+
169+
execute_coroutine_with_sync_worker(main())
170+
```
171+
172+
This migration environment class, `MyCustomMigrationEnv`, inherits from `AlembicEnvMigrationBase`
173+
and provides the necessary methods for offline and online migrations.
174+
It utilizes the `EllarSQLService` to obtain the database engines and metadata for the migration process.
175+
The `main` function initializes and executes the migration class, with specific handling for offline and online modes.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy