Skip to content

Commit a63e2a0

Browse files
committed
feat: wip
1 parent d3c124c commit a63e2a0

File tree

9 files changed

+273
-256
lines changed

9 files changed

+273
-256
lines changed

.github/workflows/tests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
strategy:
2020
fail-fast: false
2121
matrix:
22-
python-version: ['3.8', '3.9', '3.10', '3.11']
22+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
2323

2424
steps:
2525
- uses: actions/checkout@main

.pre-commit-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ fail_fast: true
44

55
repos:
66
- repo: https://github.com/pre-commit/pre-commit-hooks
7-
rev: v4.4.0
7+
rev: v5.0.0
88
hooks:
99
- id: check-added-large-files
1010
- id: check-ast
@@ -19,12 +19,12 @@ repos:
1919
- id: trailing-whitespace
2020

2121
- repo: https://github.com/psf/black
22-
rev: 23.1.0
22+
rev: 24.10.0
2323
hooks:
2424
- id: black
2525

2626
- repo: https://github.com/python-poetry/poetry
27-
rev: '1.5.1'
27+
rev: '1.8.0'
2828
hooks:
2929
- id: poetry-check
3030
- id: poetry-lock

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ integration with the
1212

1313
## Requirements
1414

15-
* python >= 3.7
15+
* python >= 3.9
1616

1717
## Installation
1818

marshmallow_peewee/convert.py

+20-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from collections import OrderedDict
4-
from typing import TYPE_CHECKING, List, Optional, Type, cast, overload
4+
from typing import TYPE_CHECKING, Optional, cast, overload
55

66
import peewee as pw
77
from marshmallow import ValidationError, fields
@@ -36,34 +36,35 @@ def __init__(self, opts: SchemaOpts):
3636
def get_fields(self, model: pw.Model) -> OrderedDict[str, fields.Field]:
3737
result = OrderedDict()
3838
meta = cast(pw.Metadata, model._meta) # type: ignore[]
39+
id_keys = self.opts.id_keys
3940
for field in meta.sorted_fields:
40-
name = field.name
41-
if self.opts.id_keys and isinstance(
41+
data_key = field.name
42+
if id_keys and isinstance(
4243
field, (pw.ForeignKeyField, pw.DeferredForeignKey)
4344
):
44-
name = field.column_name
45+
data_key = field.column_name
4546

46-
ma_field = self.convert(field)
47-
if ma_field:
48-
result[name] = ma_field
47+
ma_field = self.convert(field, data_key)
48+
if not ma_field:
49+
continue
50+
51+
result[data_key] = ma_field
4952

5053
return result
5154

5255
@overload
5356
@classmethod
54-
def register(cls, field: Type[pw.Field]) -> Callable[[Callable], Callable]:
55-
...
57+
def register(cls, field: type[pw.Field]) -> Callable[[Callable], Callable]: ...
5658

5759
@overload
5860
@classmethod
59-
def register(cls, field: Type[pw.Field], ma_field: Type[fields.Field]) -> None:
60-
...
61+
def register(cls, field: type[pw.Field], ma_field: type[fields.Field]) -> None: ...
6162

6263
@classmethod
6364
def register(
6465
cls,
65-
field: Type[pw.Field],
66-
ma_field: Optional[Type[fields.Field]] = None,
66+
field: type[pw.Field],
67+
ma_field: Optional[type[fields.Field]] = None,
6768
) -> Callable[[Callable], Callable] | None:
6869
if ma_field is None:
6970

@@ -78,10 +79,10 @@ def wrapper(fn):
7879

7980
return None
8081

81-
def convert(self, field: pw.Field) -> fields.Field:
82+
def convert(self, field: pw.Field, data_key: Optional[str] = None) -> fields.Field:
8283
params = {
84+
"data_key": data_key or field.name,
8385
"allow_none": field.null,
84-
"attribute": field.name,
8586
"required": not field.null and field.default is None,
8687
"validate": [convert_value_validate(field.db_value)],
8788
}
@@ -100,8 +101,9 @@ def convert(self, field: pw.Field) -> fields.Field:
100101
labels.append(c[1])
101102
params["validate"].append(ma_validate.OneOf(choices, labels))
102103

104+
params["metadata"] = {"name": field.name}
103105
if field.help_text:
104-
params["metadata"] = {"description": field.help_text}
106+
params["metadata"]["description"] = field.help_text
105107

106108
# use first "known" field class from field class mro
107109
# so that extended field classes get converted correctly
@@ -113,7 +115,7 @@ def convert(self, field: pw.Field) -> fields.Field:
113115
return DEFAULT_BUILDER(field, self.opts, **params)
114116

115117

116-
def generate_builder(ma_field_cls: Type[fields.Field]) -> Callable:
118+
def generate_builder(ma_field_cls: type[fields.Field]) -> Callable:
117119
"""Generate builder function for given marshmallow field."""
118120

119121
def builder(_: pw.Field, __: SchemaOpts, **params) -> fields.Field:
@@ -153,7 +155,7 @@ def convert_charfield(
153155
field: pw.CharField,
154156
_: SchemaOpts,
155157
*,
156-
validate: Optional[List] = None,
158+
validate: Optional[list] = None,
157159
**params,
158160
) -> fields.Field:
159161
if validate is None:

marshmallow_peewee/fields.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import Any, Dict, Optional, Type, Union
3+
from typing import Any, Optional, Union
44

55
import peewee as pw
66
from marshmallow import Schema, fields
@@ -9,8 +9,8 @@
99
class Related(fields.Nested):
1010
def __init__(
1111
self,
12-
nested: Optional[Type[Schema]] = None,
13-
meta: Optional[Dict[str, Any]] = None,
12+
nested: Optional[type[Schema]] = None,
13+
meta: Optional[dict[str, Any]] = None,
1414
**kwargs,
1515
):
1616
self.field = None
@@ -34,14 +34,14 @@ def init_model(self, model: pw.Model, name: str):
3434
meta = type("Meta", (), self.meta)
3535
self.nested = type("Schema", (ModelSchema,), {"Meta": meta})
3636

37-
def _deserialize(self, value, attr, data, **_):
37+
def _deserialize(self, value, attr, data, partial=None, **_):
3838
if self.field is None:
3939
raise RuntimeError("Init model first.")
4040

4141
if not isinstance(value, dict):
4242
return self.field.rel_field.python_value(value)
4343

44-
return super(Related, self)._deserialize(value, attr, data)
44+
return super(Related, self)._deserialize(value, attr, data, partial=partial)
4545

4646

4747
class ForeignKey(fields.Raw):
@@ -53,8 +53,7 @@ def _bind_to_schema(self, field_name, schema):
5353

5454
def get_value(self, obj: pw.Model, attr, **_) -> Any: # type: ignore[override]
5555
"""Return the value for a given key from an object."""
56-
check_key = attr if self.attribute is None else self.attribute
57-
value = obj.__data__.get(check_key)
56+
value = obj.__data__.get(self.metadata["name"])
5857
if value is not None and self.string_keys:
5958
return str(value)
6059
return value
@@ -63,14 +62,14 @@ def get_value(self, obj: pw.Model, attr, **_) -> Any: # type: ignore[override]
6362
class FKNested(fields.Nested):
6463
"""Get an related instance from cache."""
6564

66-
def __init__(self, nested: Union[Type[Schema], Type[pw.Model]], **kwargs):
65+
def __init__(self, nested: Union[type[Schema], type[pw.Model]], **kwargs):
6766
if issubclass(nested, pw.Model):
6867
nested = self.get_schema(nested, **kwargs)
6968

7069
super(FKNested, self).__init__(nested, **kwargs)
7170

7271
@staticmethod
73-
def get_schema(model_cls: Type[pw.Model], **kwargs) -> Type[Schema]:
72+
def get_schema(model_cls: type[pw.Model], **kwargs) -> type[Schema]:
7473
from .schema import ModelSchema
7574

7675
class Schema(ModelSchema):

marshmallow_peewee/schema.py

+14-19
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,12 @@
33
from typing import (
44
TYPE_CHECKING,
55
Any,
6-
Dict,
6+
ClassVar,
77
Generic,
88
Iterable,
9-
List,
109
Literal,
1110
Mapping,
1211
Optional,
13-
Type,
1412
Union,
1513
overload,
1614
)
@@ -26,11 +24,11 @@
2624

2725

2826
class SchemaOpts(ma.SchemaOpts, Generic[TVModel]):
29-
model: Optional[Type[TVModel]]
27+
model: Optional[type[TVModel]]
3028
dump_only_pk: bool
3129
string_keys: bool
3230
id_keys: bool
33-
model_converter: Type[DefaultConverter]
31+
model_converter: type[DefaultConverter]
3432

3533
def __init__(self, meta, **kwargs):
3634
super(SchemaOpts, self).__init__(meta, **kwargs)
@@ -73,7 +71,7 @@ def __new__(cls, name, bases, attrs):
7371
return super(SchemaMeta, cls).__new__(cls, name, bases, attrs)
7472

7573
@classmethod
76-
def get_declared_fields(cls, klass, cls_fields, inherited_fields, dict_cls):
74+
def get_declared_fields(cls, klass, cls_fields, inherited_fields, dict_cls=dict):
7775
opts: SchemaOpts = klass.opts
7876
base_fields = super(SchemaMeta, cls).get_declared_fields(
7977
klass, cls_fields, inherited_fields, dict_cls
@@ -95,20 +93,21 @@ class ModelSchema(ma.Schema, Generic[TVModel], metaclass=SchemaMeta):
9593
OPTIONS_CLASS = SchemaOpts
9694

9795
opts: SchemaOpts[TVModel]
96+
Meta: ClassVar[type[Any]]
9897

9998
def __init__(self, instance: Optional[TVModel] = None, **kwargs):
10099
self.instance = instance
101100
super(ModelSchema, self).__init__(**kwargs)
102101

103102
@overload # type: ignore[override]
104-
def load(self, data, *, many: Optional[Literal[False]] = None, **kwargs) -> TVModel:
105-
...
103+
def load(
104+
self, data, *, many: Optional[Literal[False]] = None, **kwargs
105+
) -> TVModel: ...
106106

107107
@overload
108108
def load(
109109
self, data, *, many: Optional[Literal[True]] = None, **kwargs
110-
) -> List[TVModel]:
111-
...
110+
) -> list[TVModel]: ...
112111

113112
def load(
114113
self,
@@ -121,7 +120,7 @@ def load(
121120
return super().load(data, **kwargs)
122121

123122
@ma.post_load
124-
def make_instance(self, data: Dict[str, Any], **params) -> Union[Dict, TVModel]:
123+
def make_instance(self, data: dict[str, Any], **params) -> Union[dict, TVModel]:
125124
"""Build object from data."""
126125
if not self.opts.model:
127126
return data
@@ -137,18 +136,14 @@ def make_instance(self, data: Dict[str, Any], **params) -> Union[Dict, TVModel]:
137136
if TYPE_CHECKING:
138137

139138
@overload # type: ignore[override]
140-
def dump(self, obj) -> Dict[str, Any]:
141-
...
139+
def dump(self, obj) -> dict[str, Any]: ...
142140

143141
@overload
144-
def dump(self, obj, *, many: Literal[False]) -> Dict[str, Any]:
145-
...
142+
def dump(self, obj, *, many: Literal[False]) -> dict[str, Any]: ...
146143

147144
@overload
148-
def dump(self, obj, *, many: Literal[True]) -> List[Dict[str, Any]]:
149-
...
145+
def dump(self, obj, *, many: Literal[True]) -> list[dict[str, Any]]: ...
150146

151147
def dump(
152148
self, obj: Union[TVModel, Iterable[TVModel]], *, many: Optional[bool] = None
153-
) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
154-
...
149+
) -> Union[dict[str, Any], list[dict[str, Any]]]: ...

0 commit comments

Comments
 (0)