Skip to content
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
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Version 5.2.0

* Remove actions classes.

## Version 5.1.3

* Fix `paginate` method from `PageMixin`.
Expand Down
63 changes: 11 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ Web-framework independent CRUD tools for working with database via SQLAlchemy.
## Features

* DBAL - database access layer.
* Actions templates.
* CRUD methods for create, read, update and delete object from database.
* Bulk methods for create, read, update and delete object from database.
* Method of paginating data.
* StatementMaker class for create query 'per-one-model'.
* Marshmallow (https://github.com/marshmallow-code/marshmallow) schemas for serialization input data.
* Marshmallow (https://github.com/marshmallow-code/marshmallow) schemas for serialization input data for pagination.
* Marshmallow schemas for deserialization SQLAlchemy result object to `dict`.
* Datetime with UTC timezone validation in `BaseSchema`.

Expand All @@ -38,12 +38,9 @@ $ pip install -U db_first
### Full example

```python
from db_first.actions import BaseAction
from db_first.base_model import ModelMixin
from db_first.dbal import SqlaDBAL
from db_first.dbal.exceptions import DBALObjectNotFoundException
from marshmallow import fields
from marshmallow import Schema
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Mapped
Expand All @@ -67,57 +64,19 @@ class ItemsDBAL(SqlaDBAL[Items]):
"""Items DBAL."""


class ItemSchema(Schema):
id = fields.UUID()
data = fields.String()
created_at = fields.DateTime()


class CreateItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(exclude=['id', 'created_at']).load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).create(**self._data)


class ReadItemAction(BaseAction):
def validate(self) -> None:
ItemSchema().load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).read(**self._data)


class UpdateItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(only=['id', 'data']).load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).update(**self._data)


class DeleteItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(only=['id']).load(self._data)

def action(self) -> None:
return ItemsDBAL(self._session).delete(**self._data)


if __name__ == '__main__':
new_item = CreateItemAction(session, {'data': 'data'}).run()
print('New item:', new_item)
new_item = ItemsDBAL(session).create(data='data')
print('=>', 'New item:', new_item)

item = ReadItemAction(session, {'id': new_item.id}).run()
print('Item:', item)
item = ItemsDBAL(session).read(id=new_item.id)
print('=>', 'Item:', item)

updated_item = UpdateItemAction(session, {'id': new_item.id, 'data': 'updated_data'}).run()
print('Updated item:', updated_item)
updated_item = ItemsDBAL(session).update(id=new_item.id, data='updated_data')
print('=>', 'Updated item:', updated_item)

DeleteItemAction(session, {'id': new_item.id}).run()
ItemsDBAL(session).delete(id=new_item.id)
try:
item = ReadItemAction(session, {'id': new_item.id}).run()
ItemsDBAL(session).read(id=new_item.id)
except DBALObjectNotFoundException:
print('Deleted item.')
print('=>', 'Deleted item.')
```
59 changes: 9 additions & 50 deletions examples/full_example.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
from db_first.actions import BaseAction
from db_first.base_model import ModelMixin
from db_first.dbal import SqlaDBAL
from db_first.dbal.exceptions import DBALObjectNotFoundException
from marshmallow import fields
from marshmallow import Schema
from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Mapped
Expand All @@ -27,56 +24,18 @@ class ItemsDBAL(SqlaDBAL[Items]):
"""Items DBAL."""


class ItemSchema(Schema):
id = fields.UUID()
data = fields.String()
created_at = fields.DateTime()


class CreateItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(exclude=['id', 'created_at']).load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).create(**self._data)


class ReadItemAction(BaseAction):
def validate(self) -> None:
ItemSchema().load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).read(**self._data)


class UpdateItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(only=['id', 'data']).load(self._data)

def action(self) -> Items:
return ItemsDBAL(self._session).update(**self._data)


class DeleteItemAction(BaseAction):
def validate(self) -> None:
ItemSchema(only=['id']).load(self._data)

def action(self) -> None:
return ItemsDBAL(self._session).delete(**self._data)


if __name__ == '__main__':
new_item = CreateItemAction(session, {'data': 'data'}).run()
print('New item:', new_item)
new_item = ItemsDBAL(session).create(data='data')
print('=>', 'New item:', new_item)

item = ReadItemAction(session, {'id': new_item.id}).run()
print('Item:', item)
item = ItemsDBAL(session).read(id=new_item.id)
print('=>', 'Item:', item)

updated_item = UpdateItemAction(session, {'id': new_item.id, 'data': 'updated_data'}).run()
print('Updated item:', updated_item)
updated_item = ItemsDBAL(session).update(id=new_item.id, data='updated_data')
print('=>', 'Updated item:', updated_item)

DeleteItemAction(session, {'id': new_item.id}).run()
ItemsDBAL(session).delete(id=new_item.id)
try:
item = ReadItemAction(session, {'id': new_item.id}).run()
ItemsDBAL(session).read(id=new_item.id)
except DBALObjectNotFoundException:
print('Deleted item.')
print('=>', 'Deleted item.')
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ license = {file = "LICENSE"}
name = "DB-First"
readme = "README.md"
requires-python = ">=3.11"
version = "5.1.3"
version = "5.2.0"

[project.optional-dependencies]
dev = [
Expand Down
4 changes: 0 additions & 4 deletions src/db_first/actions/__init__.py

This file was deleted.

55 changes: 0 additions & 55 deletions src/db_first/actions/base.py

This file was deleted.

43 changes: 0 additions & 43 deletions src/db_first/actions/web.py

This file was deleted.

5 changes: 1 addition & 4 deletions src/db_first/base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from datetime import datetime
from datetime import timezone

from marshmallow import Schema
from sqlalchemy import DateTime
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
Expand All @@ -20,8 +19,6 @@ def make_datetime_with_utc() -> datetime:
class ModelMixin:
"""Mixin for table model."""

_to_dict_schemas: Schema

id: Mapped[uuid.UUID] = mapped_column(
primary_key=True, nullable=False, default=make_uuid4, comment='UUID'
)
Expand All @@ -36,7 +33,7 @@ class ModelMixin:
)

@staticmethod
def validate_utc_timezone(key: str, value: datetime or None) -> datetime:
def validate_utc_timezone(key: str, value: datetime) -> datetime:
time_zone = getattr(value, 'tzinfo', None)
if time_zone != timezone.utc:
raise ValueError(
Expand Down
15 changes: 8 additions & 7 deletions src/db_first/dbal/paginate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Any

import sqlalchemy as sa
from db_first.dbal.exceptions import DBALPaginateException
from db_first.statement_maker import StatementMaker
from sqlalchemy import func

Expand Down Expand Up @@ -40,7 +41,7 @@ def query_string_to_sql_json(
ids: list[str] | None = None,
**params: dict[str, Any],
) -> dict[str, Any]:
sql_as_json = {'limit': per_page, 'offset': per_page * page}
sql_as_json = {'limit': per_page, 'offset': (page - 1) * per_page}

order_by, filters = self._extract_expressions(params)

Expand All @@ -62,17 +63,17 @@ def run_query(self, **data: dict[str, Any]):
def paginate(
self,
ids: list[str] | None = None,
page: int | None = None,
per_page: int | None = None,
page: int = 1,
per_page: int = 20,
include_metadata: bool = False,
**data: dict[str, Any],
) -> dict[str, Any]:

if page is None or page <= 0:
page = 0
if page < 1:
raise DBALPaginateException(f'Page <{page}> must be greater <1>.')

if per_page is None or per_page <= 0:
per_page = 1000
if per_page < 1:
raise DBALPaginateException(f'Page <{per_page}> must be greater <1>.')

sql_as_json = self.query_string_to_sql_json(ids=ids, page=page, per_page=per_page, **data)

Expand Down
4 changes: 2 additions & 2 deletions src/db_first/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from db_first.schemas.base import BaseSchema
from db_first.schemas.paginate import PaginateActionSchema
from db_first.schemas.paginate import PaginateResultSchema
from db_first.schemas.paginate import PaginateSchema

__all__ = ['BaseSchema', 'PaginateActionSchema', 'PaginateResultSchema']
__all__ = ['BaseSchema', 'PaginateSchema', 'PaginateResultSchema']
5 changes: 3 additions & 2 deletions src/db_first/schemas/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime
from datetime import timezone
from typing import Any

from marshmallow import post_dump
from marshmallow import RAISE
Expand All @@ -15,7 +16,7 @@ class Meta:
unknown = RAISE

@post_dump()
def _delete_keys_with_empty_value(self, data, many=False) -> dict or list:
def _delete_keys_with_empty_value(self, data: Any, many: bool = False) -> Any:
"""Clearing hierarchical structures from empty values.

Cleaning occurs for objects of the list and dict types, other types do not clean.
Expand Down Expand Up @@ -50,7 +51,7 @@ def _delete_keys_with_empty_value(self, data, many=False) -> dict or list:
return data

@staticmethod
def validate_utc_timezone(key: str, value: datetime or None) -> None:
def validate_utc_timezone(key: str, value: datetime) -> None:
time_zone = getattr(value, 'tzinfo', None)
if time_zone != timezone.utc:
raise ValueError(
Expand Down
Loading
Loading