diff --git a/CHANGES.md b/CHANGES.md index 850d2d3..6d5ccef 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## Version 5.2.0 + +* Remove actions classes. + ## Version 5.1.3 * Fix `paginate` method from `PageMixin`. diff --git a/README.md b/README.md index fc5d089..ee8962a 100644 --- a/README.md +++ b/README.md @@ -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`. @@ -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 @@ -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.') ``` diff --git a/examples/full_example.py b/examples/full_example.py index fcbb4c7..42ae6fd 100644 --- a/examples/full_example.py +++ b/examples/full_example.py @@ -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 @@ -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.') diff --git a/pyproject.toml b/pyproject.toml index 7d529b0..205cbcc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 = [ diff --git a/src/db_first/actions/__init__.py b/src/db_first/actions/__init__.py deleted file mode 100644 index aea20f0..0000000 --- a/src/db_first/actions/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from db_first.actions.base import BaseAction -from db_first.actions.web import BaseWebAction - -__all__ = ['BaseAction', 'BaseWebAction'] diff --git a/src/db_first/actions/base.py b/src/db_first/actions/base.py deleted file mode 100644 index a4dea2b..0000000 --- a/src/db_first/actions/base.py +++ /dev/null @@ -1,55 +0,0 @@ -from abc import ABC -from abc import abstractmethod -from typing import Any - -from sqlalchemy.orm import Session - - -class BaseAction(ABC): - """The base class for all Actions that implement logic.""" - - _session: Session - _data: dict[str, Any] - validated_data: dict[str, Any] - result: dict[str, Any] - - def __init__(self, session: Session, data: dict[str, Any]): - """Init method for Action. - - self._session - database session object. - self._data - input data for action. - self.result - result of data processing and performed actions. - """ - self._session = session - self._data = data - - @abstractmethod - def validate(self) -> Any: - """Validate called by `self.run()` to validate data. - - Will raise exception if validation fails. - - :raises: ActionValidationException - """ - raise NotImplementedError('Implement method for validate input data.') - - @abstractmethod - def action(self) -> Any: - """Action called by `self.run()` to execute logic. - - Will raise exception if action fails. - - :raises: ActionException - """ - raise NotImplementedError('Implement logic here.') - - def run(self) -> Any: - """Run executes the Action. - - Can raise functions exceptions. - - :raises: ActionRunException - """ - self.validated_data = self.validate() - self.result = self.action() - return self.result diff --git a/src/db_first/actions/web.py b/src/db_first/actions/web.py deleted file mode 100644 index be5d80c..0000000 --- a/src/db_first/actions/web.py +++ /dev/null @@ -1,43 +0,0 @@ -from abc import abstractmethod -from typing import Any - -from db_first.actions import BaseAction - - -class BaseWebAction(BaseAction): - """The base class for all Actions that implement logic.""" - - serialized_result: dict[str, Any] - - @abstractmethod - def permit(self) -> None: - """Permit called by `self.run()` to check access permissions. - - Will raise exception if permit fails. - - :raises: ActionPermitException - """ - raise NotImplementedError('Implement method for checking permissions.') - - @abstractmethod - def serialization(self) -> Any: - """Serialization called by `self.run()` to serialize result. - - Will raise exception if serialization fails. - - :raises: ActionSerializationException - """ - raise NotImplementedError('Implement method for serialization output data.') - - def run(self) -> Any: - """Run executes the Action. - - Can raise functions exceptions. - - :raises: ActionRunException - """ - self.permit() - self.validated_data = self.validate() - self.result = self.action() - self.serialized_result = self.serialization() - return self.serialized_result diff --git a/src/db_first/base_model.py b/src/db_first/base_model.py index 52004b2..f05cc82 100644 --- a/src/db_first/base_model.py +++ b/src/db_first/base_model.py @@ -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 @@ -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' ) @@ -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( diff --git a/src/db_first/dbal/paginate.py b/src/db_first/dbal/paginate.py index 03cf8fa..8554dc3 100644 --- a/src/db_first/dbal/paginate.py +++ b/src/db_first/dbal/paginate.py @@ -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 @@ -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) @@ -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) diff --git a/src/db_first/schemas/__init__.py b/src/db_first/schemas/__init__.py index 3c812f3..33b2b46 100644 --- a/src/db_first/schemas/__init__.py +++ b/src/db_first/schemas/__init__.py @@ -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'] diff --git a/src/db_first/schemas/base.py b/src/db_first/schemas/base.py index dc34e80..ad20a13 100644 --- a/src/db_first/schemas/base.py +++ b/src/db_first/schemas/base.py @@ -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 @@ -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. @@ -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( diff --git a/src/db_first/schemas/paginate.py b/src/db_first/schemas/paginate.py index ea678e0..67a1f25 100644 --- a/src/db_first/schemas/paginate.py +++ b/src/db_first/schemas/paginate.py @@ -8,7 +8,7 @@ from marshmallow import ValidationError -class PaginateActionSchema(BaseSchema): +class PaginateSchema(BaseSchema): class Meta: unknown = INCLUDE diff --git a/tests/conftest.py b/tests/conftest.py index 8132eef..b99d1f1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,10 +3,7 @@ import pytest from db_first import ModelMixin -from db_first.actions import BaseAction -from db_first.actions import BaseWebAction from db_first.dbal import SqlaDBAL -from db_first.schemas import PaginateActionSchema from db_first.statement_maker import StatementMaker from sqlalchemy import create_engine from sqlalchemy import ForeignKey @@ -19,10 +16,7 @@ from sqlalchemy.orm import relationship from sqlalchemy.orm import Session -from tests.contrib.schemas import ChildSchema -from tests.contrib.schemas import FatherSchema from tests.contrib.schemas import ParentPaginationSchema -from tests.contrib.schemas import ParentSchema UNIQUE_STRING = (f'name_{number}' for number in range(1_000)) @@ -68,8 +62,6 @@ class Children(Base, ModelMixin): class Fathers(Base, ModelMixin): __tablename__ = 'fathers' - _to_dict_schemas = ParentSchema - first: Mapped[str] = mapped_column() second: Mapped[str | None] = mapped_column() @@ -89,120 +81,80 @@ class ParentsDBAL(SqlaDBAL[parents_model]): @pytest.fixture(scope='session') -def fx_parent_action__paginate(fx_db, fx_parent_dbal): +def fx_parent__paginate(fx_db, fx_parent_dbal): session_db, parents_model, _, _ = fx_db - def _create_action(data: dict[str, Any]): - class PaginateAction(BaseWebAction): - def permit(self) -> None: - pass - - def validate(self) -> None: - PaginateActionSchema().load(self._data) + def _f(data: dict[str, Any]): + params = {k: v for k, v in data.items() if k not in ['fields']} - def action(self) -> dict[str, Any]: - params = {k: v for k, v in self._data.items() if k not in ['fields']} - result = fx_parent_dbal(session_db).paginate(**params) - return result + result = fx_parent_dbal(session_db).paginate(**params) - def serialization(self) -> None: - only = self._data.get('fields') - serialized_result = ParentPaginationSchema(only=only).dump(self.result) - return serialized_result + only = data.get('fields') + serialized_result = ParentPaginationSchema(only=only).dump(result) - return PaginateAction(session_db, data) + return serialized_result - return _create_action + return _f @pytest.fixture(scope='session') -def fx_parent_action__create(fx_db, fx_parent_dbal): +def fx_parent__create(fx_db, fx_parent_dbal): session_db, parents_model, _, _ = fx_db - def _create_action(data: dict[str, Any]): - class CreateAction(BaseAction): - def validate(self) -> None: - ParentSchema(exclude=['id']).load(self._data) - - def action(self) -> None: - return fx_parent_dbal(session_db).create(**self._data) + def _f(data: dict[str, Any]): + return fx_parent_dbal(session_db).create(**data) - return CreateAction(session_db, data) - - return _create_action + return _f @pytest.fixture(scope='session') -def fx_parent_action__update(fx_db, fx_parent_dbal): +def fx_parent__update(fx_db, fx_parent_dbal): session_db, parents_model, _, _ = fx_db - def _create_action(data: dict[str, Any]): - class UpdateAction(BaseAction): - def validate(self) -> None: - ParentSchema().load(self._data) - - def action(self) -> None: - return fx_parent_dbal(session_db).update(**self._data) + def _f(data: dict[str, Any]): + return fx_parent_dbal(session_db).update(**data) - return UpdateAction(session_db, data) - - return _create_action + return _f @pytest.fixture(scope='session') -def fx_child_action__create(fx_db, fx_parent_dbal): +def fx_child__create(fx_db, fx_parent_dbal): session_db, _, child_model, _ = fx_db class ChildDBAL(SqlaDBAL[child_model]): """DBAL for Children.""" - def _create_action(data: dict[str, Any]): - class CreateAction(BaseAction): - def validate(self) -> None: - ChildSchema(exclude=['id']).load(self._data) - - def action(self) -> None: - return ChildDBAL(session_db).create(**self._data) - - return CreateAction(session_db, data) + def _f(data: dict[str, Any]): + return ChildDBAL(session_db).create(**data) - return _create_action + return _f @pytest.fixture(scope='session') -def fx_father_action__create(fx_db, fx_parent_dbal): +def fx_father__create(fx_db, fx_parent_dbal): session_db, _, _, father_model = fx_db class FathersDBAL(SqlaDBAL[father_model]): """DBAL for Fathers.""" - def _create_action(data: dict[str, Any]): - class CreateAction(BaseAction): - def validate(self) -> None: - FatherSchema(exclude=['id']).load(self._data) - - def action(self) -> None: - return FathersDBAL(session_db).create(**self._data) - - return CreateAction(session_db, data) + def _f(data: dict[str, Any]): + return FathersDBAL(session_db).create(**data) - return _create_action + return _f @pytest.fixture -def fx_parents__non_deletion( - fx_parent_action__create, fx_child_action__create, fx_father_action__create -): +def fx_parents__non_deletion(fx_parent__create, fx_child__create, fx_father__create): def _create_item() -> Result: - new_father = fx_father_action__create({'first': next(UNIQUE_STRING)}).run() + new_father = fx_father__create({'first': next(UNIQUE_STRING)}) parent_data = { 'first': next(UNIQUE_STRING), 'second': f'full {next(UNIQUE_STRING)}', 'father_id': new_father.id, } - new_parent = fx_parent_action__create(parent_data).run() - fx_child_action__create({'first': next(UNIQUE_STRING), 'parent_id': new_parent.id}).run() + new_parent = fx_parent__create(parent_data) + fx_child__create({'first': next(UNIQUE_STRING), 'parent_id': new_parent.id}) return new_parent return _create_item diff --git a/tests/test_actions.py b/tests/test_actions.py deleted file mode 100644 index 970c7bb..0000000 --- a/tests/test_actions.py +++ /dev/null @@ -1,51 +0,0 @@ -from copy import deepcopy - -from db_first import ModelMixin -from db_first.actions import BaseWebAction -from db_first.dbal import SqlaDBAL -from db_first.schemas import BaseSchema -from marshmallow import fields -from sqlalchemy.orm import Mapped -from sqlalchemy.orm import mapped_column - -from tests.conftest import UNIQUE_STRING - - -class TestSchema(BaseSchema): - id = fields.UUID() - first = fields.String() - - -def test_actions(fx_db_connection): - Base, engine, db_session = fx_db_connection - - class TestModel(Base, ModelMixin): - __tablename__ = 'test_models' - - first: Mapped[str] = mapped_column() - - Base.metadata.create_all(engine) - - class TestModelDBAL(SqlaDBAL[TestModel]): - """DBAL for TestModel.""" - - class CreateTestAction(BaseWebAction): - def permit(self): - pass - - def validate(self): - TestSchema(only=['first']).load(self._data) - - def action(self): - new_test_obj = TestModelDBAL(self._session).create(**self._data) - return new_test_obj - - def serialization(self): - serialized_test_obj = TestSchema().dump(self.result) - return serialized_test_obj - - data_for_create = {'first': next(UNIQUE_STRING)} - new_data = CreateTestAction(db_session, data_for_create).run() - new_data_for_assert = deepcopy(new_data) - assert new_data_for_assert.pop('id') - assert new_data_for_assert == data_for_create diff --git a/tests/test_pagination_mixin.py b/tests/test_pagination_mixin.py index 53cfb3c..328bbe5 100644 --- a/tests/test_pagination_mixin.py +++ b/tests/test_pagination_mixin.py @@ -2,17 +2,17 @@ from math import ceil from uuid import UUID -import marshmallow import pytest +from db_first.dbal.exceptions import DBALPaginateException from tests.conftest import UNIQUE_STRING -def test_controller__pagination(fx_parent_action__create, fx_parent_action__paginate): - ids = [fx_parent_action__create({'first': next(UNIQUE_STRING)}).run().id for _ in range(3)] +def test_pagination__pagination(fx_parent__create, fx_parent__paginate): + ids = [fx_parent__create({'first': next(UNIQUE_STRING)}).id for _ in range(3)] - data = {'page': 0, 'per_page': 2, 'ids': ids, 'fields': ['items.id', 'items.first']} - items = fx_parent_action__paginate(data).run() + data = {'page': 1, 'per_page': 2, 'ids': ids, 'fields': ['items.id', 'items.first']} + items = fx_parent__paginate(data) assert items['items'] assert len(items['items']) == 2 @@ -21,72 +21,68 @@ def test_controller__pagination(fx_parent_action__create, fx_parent_action__pagi assert isinstance(item['first'], str) data = {'page': 1, 'per_page': 2, 'ids': ids, 'fields': ['items.id', 'items.first']} - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) assert items['items'] - assert len(items['items']) == 1 + assert len(items['items']) == 2 for item in items['items']: assert UUID(item['id']) assert isinstance(item['first'], str) -def test_controller__sorting(fx_parent_action__create, fx_parent_action__paginate): - ids = [fx_parent_action__create({'first': next(UNIQUE_STRING)}).run().id for _ in range(3)] +def test_pagination__sorting(fx_parent__create, fx_parent__paginate): + ids = [fx_parent__create({'first': next(UNIQUE_STRING)}).id for _ in range(3)] data = {'ids': ids, 'sort__created_at': 'asc'} - items_asc = fx_parent_action__paginate(data).run() + items_asc = fx_parent__paginate(data) asc_ids = [item['id'] for item in items_asc['items']] data = {'ids': ids, 'sort__created_at': 'desc'} - items_desc = fx_parent_action__paginate(data).run() + items_desc = fx_parent__paginate(data) desc_ids = [item['id'] for item in items_desc['items']] assert asc_ids[0] == desc_ids[-1] -def test_controller__searching(fx_parent_action__create, fx_parent_action__paginate): - new_item = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() +def test_pagination__searching(fx_parent__create, fx_parent__paginate): + new_item = fx_parent__create({'first': next(UNIQUE_STRING)}) + fx_parent__create({'first': next(UNIQUE_STRING)}) search_substring = new_item.first[3:] data = {'fields': ['items.id', 'items.first'], 'contain__first': search_substring} - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) for item in items['items']: assert search_substring in item['first'] -def test_controller__get_fields_of_list( - fx_db, fx_parent_action__create, fx_parent_action__paginate -): +def test_pagination__get_fields_of_list(fx_db, fx_parent__create, fx_parent__paginate): _, _, _, Fathers = fx_db - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() + fx_parent__create({'first': next(UNIQUE_STRING)}) data = {'fields': ['items.id', 'items.first']} - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) assert list(items['items'][0]) == ['id', 'first'] -def test_controller__filtrating( - fx_parent_action__create, fx_parent_action__update, fx_parent_action__paginate -): - first_item = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - _ = [fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() for _ in range(10)] +def test_pagination__filtrating(fx_parent__create, fx_parent__update, fx_parent__paginate): + first_item = fx_parent__create({'first': next(UNIQUE_STRING)}) + _ = [fx_parent__create({'first': next(UNIQUE_STRING)}) for _ in range(10)] patched_item_payload = {'id': first_item.id, 'first': 'first for test filtrating'} - patched_first_item = fx_parent_action__update(patched_item_payload).run() + patched_first_item = fx_parent__update(patched_item_payload) data = {'eq__first': patched_first_item.first} - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) assert len(items['items']) == 1 assert items['items'][0]['id'] == str(first_item.id) -def test_controller__interval_filtration(fx_parent_action__create, fx_parent_action__paginate): - item_1 = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - item_2 = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - item_3 = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() +def test_pagination__interval_filtration(fx_parent__create, fx_parent__paginate): + item_1 = fx_parent__create({'first': next(UNIQUE_STRING)}) + item_2 = fx_parent__create({'first': next(UNIQUE_STRING)}) + item_3 = fx_parent__create({'first': next(UNIQUE_STRING)}) data = { 'ids': [item_1.id, item_2.id, item_3.id], @@ -94,7 +90,7 @@ def test_controller__interval_filtration(fx_parent_action__create, fx_parent_act 'ge__created_at': item_1.created_at.replace(tzinfo=timezone.utc), 'le__created_at': item_2.created_at.replace(tzinfo=timezone.utc), } - items_asc = fx_parent_action__paginate(data).run() + items_asc = fx_parent__paginate(data) assert items_asc['items'] assert len(items_asc['items']) == 2 @@ -107,7 +103,7 @@ def test_controller__interval_filtration(fx_parent_action__create, fx_parent_act 'ge__created_at': item_1.created_at.replace(tzinfo=timezone.utc), 'le__created_at': item_2.created_at.replace(tzinfo=timezone.utc), } - items_desc = fx_parent_action__paginate(data).run() + items_desc = fx_parent__paginate(data) assert items_desc['items'] assert len(items_desc['items']) == 2 @@ -117,26 +113,22 @@ def test_controller__interval_filtration(fx_parent_action__create, fx_parent_act @pytest.mark.parametrize('page', [-1]) @pytest.mark.parametrize('per_page', [-1, 0]) -def test_controller__get_non_exist_page( - fx_parent_action__create, fx_parent_action__paginate, page, per_page -): - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() +def test_pagination__get_non_exist_page(fx_parent__create, fx_parent__paginate, page, per_page): + fx_parent__create({'first': next(UNIQUE_STRING)}) data = {'page': page, 'per_page': per_page, 'include_metadata': 'enable'} - with pytest.raises(marshmallow.exceptions.ValidationError): - fx_parent_action__paginate(data).run() + with pytest.raises(DBALPaginateException): + fx_parent__paginate(data) -@pytest.mark.parametrize('page', [0, 1]) +@pytest.mark.parametrize('page', [1, 2]) @pytest.mark.parametrize('per_page', [1, 2]) -def test_controller__get_pages( - fx_parent_action__create, fx_parent_action__paginate, page, per_page -): - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() +def test_pagination__get_pages(fx_parent__create, fx_parent__paginate, page, per_page): + fx_parent__create({'first': next(UNIQUE_STRING)}) + fx_parent__create({'first': next(UNIQUE_STRING)}) data = {'page': page, 'per_page': per_page, 'include_metadata': 'enable'} - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) assert items['_metadata']['pagination']['page'] == page assert items['_metadata']['pagination']['per_page'] == per_page @@ -144,41 +136,33 @@ def test_controller__get_pages( items['_metadata']['pagination']['total'] / per_page ) assert items['_metadata']['pagination']['total'] > 1 - assert items['items'] + assert len(items['items']) == per_page -def test_controller__without_meta_pagination( - fx_db, fx_parent_action__create, fx_parent_action__paginate -): - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() - fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() +def test_pagination__without_meta_pagination(fx_db, fx_parent__create, fx_parent__paginate): + fx_parent__create({'first': next(UNIQUE_STRING)}) + fx_parent__create({'first': next(UNIQUE_STRING)}) - data = {'page': 0, 'per_page': 2} - items = fx_parent_action__paginate(data).run() + data = {'page': 1, 'per_page': 2} + items = fx_parent__paginate(data) assert '_metadata' not in items assert items['items'] -def test_controller__fields_for_relations( - fx_parent_action__create, - fx_child_action__create, - fx_father_action__create, - fx_parent_action__paginate, +def test_pagination__fields_for_relations( + fx_parent__create, + fx_child__create, + fx_father__create, + fx_parent__paginate, ): - new_father_1 = fx_father_action__create({'first': next(UNIQUE_STRING)}).run() - new_parent_1 = fx_parent_action__create( - {'first': next(UNIQUE_STRING), 'father_id': new_father_1.id} - ).run() - fx_child_action__create({'first': next(UNIQUE_STRING), 'parent_id': new_parent_1.id}).run() - - new_father = fx_father_action__create({'first': next(UNIQUE_STRING)}).run() - new_parent = fx_parent_action__create( - {'first': next(UNIQUE_STRING), 'father_id': new_father.id} - ).run() - new_child = fx_child_action__create( - {'first': next(UNIQUE_STRING), 'parent_id': new_parent.id} - ).run() + new_father_1 = fx_father__create({'first': next(UNIQUE_STRING)}) + new_parent_1 = fx_parent__create({'first': next(UNIQUE_STRING), 'father_id': new_father_1.id}) + fx_child__create({'first': next(UNIQUE_STRING), 'parent_id': new_parent_1.id}) + + new_father = fx_father__create({'first': next(UNIQUE_STRING)}) + new_parent = fx_parent__create({'first': next(UNIQUE_STRING), 'father_id': new_father.id}) + new_child = fx_child__create({'first': next(UNIQUE_STRING), 'parent_id': new_parent.id}) data = { 'fields': [ @@ -192,7 +176,7 @@ def test_controller__fields_for_relations( ], 'eq__id': new_parent.id, } - items = fx_parent_action__paginate(data).run() + items = fx_parent__paginate(data) assert len(items['items']) == 1 assert items['items'][0]['id'] == str(new_parent.id) @@ -207,13 +191,9 @@ def test_controller__fields_for_relations( ] -def test_controller__pagination__nullable(fx_parent_action__create, fx_parent_action__paginate): - _ = [fx_parent_action__create({'first': next(UNIQUE_STRING)}).run().id for _ in range(3)] +def test_pagination__pagination__nullable(fx_parent__create, fx_parent__paginate): + _ = [fx_parent__create({'first': next(UNIQUE_STRING)}).id for _ in range(3)] data = {'page': 0, 'per_page': 0, 'include_metadata': 'enable'} - items = fx_parent_action__paginate(data).run() - - assert items['items'] - assert items['_metadata']['pagination']['page'] == 0 - assert items['_metadata']['pagination']['per_page'] == 1000 - assert items['_metadata']['pagination']['pages'] == 1 + with pytest.raises(DBALPaginateException): + fx_parent__paginate(data) diff --git a/tests/test_statement_maker.py b/tests/test_statement_maker.py index 6545da7..6c5e181 100644 --- a/tests/test_statement_maker.py +++ b/tests/test_statement_maker.py @@ -11,10 +11,10 @@ def test_statement_maker__init(fx_db): assert stmt.compile().string == select(Parents).limit(1).offset(1).compile().string -def test_statement_maker__filter__lt(fx_db, fx_make_stmt, fx_parent_action__create): +def test_statement_maker__filter__lt(fx_db, fx_make_stmt, fx_parent__create): _, Parents, _, _ = fx_db - parent_1 = fx_parent_action__create({'first': next(UNIQUE_STRING)}).run() + parent_1 = fx_parent__create({'first': next(UNIQUE_STRING)}) statement_maker = fx_make_stmt( Parents,