Skip to content

Commit 094d2ee

Browse files
authored
Merge pull request #3 from flask-pro/unification-of-method-interfaces
unification of method interfaces.
2 parents fedc288 + f8983eb commit 094d2ee

File tree

11 files changed

+130
-91
lines changed

11 files changed

+130
-91
lines changed

.github/workflows/testing.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
3.10
1414
3.11
1515
3.12
16+
3.13
1617
- run: make format
1718
test:
1819
runs-on: ubuntu-latest

.pre-commit-config.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/asottile/pyupgrade
3-
rev: v3.17.0
3+
rev: v3.20.0
44
hooks:
55
- id: pyupgrade
66
args: [--py39]
@@ -9,11 +9,11 @@ repos:
99
hooks:
1010
- id: rm-unneeded-f-str
1111
- repo: https://github.com/mxr/unkey
12-
rev: v0.0.1
12+
rev: v0.0.2
1313
hooks:
1414
- id: unkey
1515
- repo: https://github.com/pre-commit/pre-commit-hooks
16-
rev: v4.6.0
16+
rev: v6.0.0
1717
hooks:
1818
- id: check-ast
1919
- id: fix-byte-order-marker
@@ -27,13 +27,13 @@ repos:
2727
- id: double-quote-string-fixer
2828
- id: check-merge-conflict
2929
- repo: https://github.com/PyCQA/docformatter
30-
rev: v1.7.5
30+
rev: v1.7.7
3131
hooks:
3232
- id: docformatter
3333
additional_dependencies: [tomli]
3434
args: [--in-place, --config, ./pyproject.toml]
3535
- repo: https://github.com/jshwi/docsig
36-
rev: v0.63.0
36+
rev: v0.71.0
3737
hooks:
3838
- id: docsig
3939
args:
@@ -43,11 +43,11 @@ repos:
4343
- --check-protected
4444
- --ignore-no-params
4545
- repo: https://github.com/psf/black
46-
rev: 24.8.0
46+
rev: 25.1.0
4747
hooks:
4848
- id: black
4949
- repo: https://github.com/asottile/reorder_python_imports
50-
rev: v3.13.0
50+
rev: v3.15.0
5151
hooks:
5252
- id: reorder-python-imports
5353
- repo: https://github.com/pre-commit/pygrep-hooks
@@ -57,22 +57,22 @@ repos:
5757
- id: python-no-eval
5858
- id: text-unicode-replacement-char
5959
- repo: https://github.com/pycqa/flake8
60-
rev: 7.1.1
60+
rev: 7.3.0
6161
hooks:
6262
- id: flake8
6363
additional_dependencies:
6464
- flake8-bugbear
6565
- flake8-implicit-str-concat
6666
args: [--max-line-length=100]
6767
- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks
68-
rev: v2.14.0
68+
rev: v2.15.0
6969
hooks:
7070
- id: pretty-format-yaml
7171
args: [--autofix, --indent, '2']
7272
- id: pretty-format-toml
7373
args: [--autofix, --indent, '2']
7474
- repo: https://codeberg.org/frnmst/md-toc
75-
rev: 8.2.3
75+
rev: 9.0.0
7676
hooks:
7777
- id: md-toc
7878
args: [-p, github]
@@ -81,7 +81,7 @@ repos:
8181
hooks:
8282
- id: checkmake
8383
- repo: https://github.com/PyCQA/bandit
84-
rev: 1.7.10
84+
rev: 1.8.6
8585
hooks:
8686
- id: bandit
8787
args: [-c, pyproject.toml]

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## Version 3.0.0
2+
3+
* Add method `paginate()` to `ReadMixin`.
4+
* Change interface for methods `read()` from `ReadMixin`, `delete()` from `DeleteMixin`.
5+
16
## Version 2.1.0
27

38
* `PaginateMixin` removed, this functional moved to `ReadMixin`.

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ CRUD tools for working with database via SQLAlchemy.
1515
## Features
1616

1717
* CreateMixin, ReadMixin, UpdateMixin, DeleteMixin for CRUD operation for database.
18-
* PaginateMixin for get paginated data from database.
19-
* QueryMaker class for create query 'per-one-model'.
20-
* Marshmallow (https://github.com/marshmallow-code/marshmallow) schemas for validating input data.
18+
* ReadMixin support paginated data from database.
19+
* StatementMaker class for create query 'per-one-model'.
20+
* Marshmallow (https://github.com/marshmallow-code/marshmallow) schemas for serialization input data.
2121
* Marshmallow schemas for deserialization SQLAlchemy result object to `dict`.
2222

2323
## Installation
@@ -97,14 +97,14 @@ class ItemController(CreateMixin, ReadMixin, UpdateMixin, DeleteMixin, BaseCRUD)
9797
if __name__ == '__main__':
9898
item = ItemController()
9999

100-
first_new_item = item.create(deserialize=True, data='first')
100+
first_new_item = item.create({'data': 'first'}, deserialize=True)
101101
print('Item as object:', first_new_item)
102-
second_new_item = item.create(deserialize=True, data='second', serialize=True)
102+
second_new_item = item.create({'data': 'second'}, deserialize=True, serialize=True)
103103
print('Item as dict:', second_new_item)
104104

105-
first_item = item.read(id=first_new_item.id)
105+
first_item = item.read({'id': first_new_item.id})
106106
print('Item as object:', first_item)
107-
first_item = item.read(id=first_new_item.id)
107+
first_item = item.read({'id': first_new_item.id})
108108
print('Item as dict:', first_item)
109109

110110
updated_first_item = item.update(data={'id': first_new_item.id, 'data': 'updated_first'})
@@ -114,9 +114,10 @@ if __name__ == '__main__':
114114
)
115115
print('Item as dict:', updated_second_item)
116116

117-
items = item.read(sort_created_at='desc')
117+
items = item.paginate(sort_created_at='desc')
118118
print('Items as objects:', items)
119-
items = item.read(sort_created_at='desc', serialize=True)
119+
items = item.paginate(sort_created_at='desc', serialize=True)
120120
print('Items as dicts:', items)
121121

122+
122123
```

examples/full_example.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ class Meta:
6060
if __name__ == '__main__':
6161
item = ItemController()
6262

63-
first_new_item = item.create(deserialize=True, data='first')
63+
first_new_item = item.create({'data': 'first'}, deserialize=True)
6464
print('Item as object:', first_new_item)
65-
second_new_item = item.create(deserialize=True, data='second', serialize=True)
65+
second_new_item = item.create({'data': 'second'}, deserialize=True, serialize=True)
6666
print('Item as dict:', second_new_item)
6767

68-
first_item = item.read(id=first_new_item.id)
68+
first_item = item.read({'id': first_new_item.id})
6969
print('Item as object:', first_item)
70-
first_item = item.read(id=first_new_item.id)
70+
first_item = item.read({'id': first_new_item.id})
7171
print('Item as dict:', first_item)
7272

7373
updated_first_item = item.update(data={'id': first_new_item.id, 'data': 'updated_first'})
@@ -77,7 +77,7 @@ class Meta:
7777
)
7878
print('Item as dict:', updated_second_item)
7979

80-
items = item.read(sort_created_at='desc')
80+
items = item.paginate(sort_created_at='desc')
8181
print('Items as objects:', items)
82-
items = item.read(sort_created_at='desc', serialize=True)
82+
items = item.paginate(sort_created_at='desc', serialize=True)
8383
print('Items as dicts:', items)

pyproject.toml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,22 @@ license = {file = "LICENSE"}
2525
name = "DB-First"
2626
readme = "README.md"
2727
requires-python = ">=3.9"
28-
version = "2.1.0"
28+
version = "3.0.0"
2929

3030
[project.optional-dependencies]
3131
dev = [
32-
"build==1.2.2",
33-
"pre-commit==3.8.0",
34-
"pytest==8.3.3",
35-
"pytest-cov==5.0.0",
36-
"python-dotenv==1.0.1",
37-
"tox==4.21.2",
38-
"twine==5.1.1"
32+
"build==1.3.0",
33+
"pre-commit==4.3.0",
34+
"pytest==8.4.2",
35+
"pytest-cov==7.0.0",
36+
"python-dotenv==1.1.1",
37+
"tox==4.30.2",
38+
"twine==6.2.0"
3939
]
4040

4141
[project.urls]
42-
changelog = "https://github.com/flask-pro/db_first/blob/master/CHANGES.md"
43-
repository = "https://github.com/flask-pro/db_first"
42+
changelog = "https://github.com/flask-pro/db-first/blob/master/CHANGES.md"
43+
repository = "https://github.com/flask-pro/db-first"
4444

4545
[tool.bandit]
4646
exclude_dirs = ["tests"]
@@ -78,6 +78,7 @@ where = ["src"]
7878
legacy_tox_ini = """
7979
[tox]
8080
env_list =
81+
py313
8182
py312
8283
py311
8384
py310

src/db_first/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def _clean_data(cls, data: Any) -> Any:
4040
:return: cleaned object.
4141
"""
4242

43-
empty_values = ('', None, [], {}, (), set())
43+
empty_values = ('', None, ..., [], {}, (), set())
4444

4545
if isinstance(data, dict):
4646
cleaned_dict = {k: cls._clean_data(v) for k, v in data.items()}

src/db_first/mixins/crud.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,12 @@ def create_object(self, **kwargs) -> Result:
4444
return new_obj
4545

4646
def create(
47-
self, deserialize: bool = False, serialize: bool = False, **kwargs: dict
47+
self, data: dict, deserialize: bool = False, serialize: bool = False
4848
) -> Result or dict:
4949
if deserialize:
50-
kwargs = self.deserialize_data('input_schema_of_create', kwargs)
50+
data = self.deserialize_data('input_schema_of_create', data)
5151

52-
new_object = self.create_object(**kwargs)
52+
new_object = self.create_object(**data)
5353

5454
if serialize:
5555
return self.serialize_data('output_schema_of_create', new_object)
@@ -146,7 +146,7 @@ def _paginate(
146146

147147
return items
148148

149-
def read(
149+
def paginate(
150150
self,
151151
page: int = 1,
152152
per_page: Optional[int] = None,
@@ -196,6 +196,27 @@ def read(
196196

197197
return items
198198

199+
def read_object(self, id: Any) -> Result:
200+
201+
session = self._get_option_from_meta('session')
202+
model = self._get_option_from_meta('model')
203+
204+
stmt = select(model).where(model.id == id)
205+
return session.scalars(stmt).one()
206+
207+
def read(
208+
self, data: dict, deserialize: bool = False, serialize: bool = False
209+
) -> Result or dict:
210+
if deserialize:
211+
data = self.deserialize_data('input_schema_of_read', data)
212+
213+
object_ = self.read_object(**data)
214+
215+
if serialize:
216+
return self.serialize_data('output_schema_of_read', object_)
217+
218+
return object_
219+
199220

200221
class UpdateMixin:
201222
"""Update object in database.
@@ -222,10 +243,8 @@ def update_object(self, id: Any, **kwargs) -> Result:
222243
session = self._get_option_from_meta('session')
223244
model = self._get_option_from_meta('model')
224245

225-
stmt = update(model).where(model.id == id).values(**kwargs)
226-
session.execute(stmt)
227-
228-
obj = session.scalars(select(model).where(model.id == id)).one()
246+
stmt = update(model).where(model.id == id).values(**kwargs).returning(model)
247+
obj = session.scalars(stmt).one()
229248
return obj
230249

231250
def update(
@@ -253,5 +272,7 @@ def delete_object(self, id: Any) -> None:
253272

254273
session.execute(delete(model).where(model.id == id))
255274

256-
def delete(self, id: Any) -> None:
257-
self.delete_object(id)
275+
def delete(self, data: dict, deserialize: bool = False) -> None:
276+
if deserialize:
277+
data = self.deserialize_data('input_schema_of_read', data)
278+
self.delete_object(**data)

tests/conftest.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,15 @@ class Meta:
216216
@pytest.fixture
217217
def fx_parents__non_deletion(fx_parent_controller, fx_child_controller, fx_father_controller):
218218
def _create_item() -> Result:
219-
new_father = fx_father_controller.create(first=next(UNIQUE_STRING))
219+
new_father = fx_father_controller.create({'first': next(UNIQUE_STRING)})
220220
new_parent = fx_parent_controller.create(
221-
first=next(UNIQUE_STRING), second=f'full {next(UNIQUE_STRING)}', father_id=new_father.id
221+
{
222+
'first': next(UNIQUE_STRING),
223+
'second': f'full {next(UNIQUE_STRING)}',
224+
'father_id': new_father.id,
225+
}
222226
)
223-
fx_child_controller.create(first=next(UNIQUE_STRING), parent_id=new_parent.id)
227+
fx_child_controller.create({'first': next(UNIQUE_STRING), 'parent_id': new_parent.id})
224228
return new_parent
225229

226230
return _create_item

0 commit comments

Comments
 (0)