Skip to content

Commit

Permalink
add example from pydantic/pydantic#2625, fix by_alias
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Apr 16, 2023
1 parent 3e5fa14 commit 0b1859c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 13 deletions.
32 changes: 21 additions & 11 deletions src/serializers/computed_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ impl ComputedFields {
extra: &Extra,
) -> Result<(), S::Error> {
for computed_field in self.0.iter() {
let property_name = computed_field.property_name.as_ref(model.py());
let property_name_py = computed_field.property_name_py.as_ref(model.py());
if let Some((next_include, next_exclude)) = filter
.key_filter(property_name, include, exclude)
.key_filter(property_name_py, include, exclude)
.map_err(py_err_se_err)?
{
let cfs = ComputedFieldSerializer {
Expand All @@ -69,7 +69,11 @@ impl ComputedFields {
exclude: next_exclude,
extra,
};
map.serialize_entry(&computed_field.alias, &cfs)?;
let key = match extra.by_alias {
true => computed_field.alias.as_str(),
false => computed_field.property_name.as_str(),
};
map.serialize_entry(key, &cfs)?;
}
}
Ok(())
Expand All @@ -78,7 +82,8 @@ impl ComputedFields {

#[derive(Debug, Clone)]
struct ComputedField {
property_name: Py<PyString>,
property_name: String,
property_name_py: Py<PyString>,
return_ob_type: Option<ObType>,
alias: String,
alias_py: Py<PyString>,
Expand All @@ -92,7 +97,8 @@ impl ComputedField {
let return_ob_type = get_json_return_type(schema)?;
let alias_py: &PyString = schema.get_as(intern!(py, "alias"))?.unwrap_or(property_name);
Ok(Self {
property_name: property_name.into_py(py),
property_name: property_name.extract()?,
property_name_py: property_name.into_py(py),
return_ob_type,
alias: alias_py.extract()?,
alias_py: alias_py.into_py(py),
Expand All @@ -109,17 +115,21 @@ impl ComputedField {
extra: &Extra,
) -> PyResult<()> {
let py = model.py();
let property_name = self.property_name.as_ref(py);
let property_name_py = self.property_name_py.as_ref(py);

if let Some((next_include, next_exclude)) = filter.key_filter(property_name, include, exclude)? {
let next_value = model.getattr(property_name)?;
if let Some((next_include, next_exclude)) = filter.key_filter(property_name_py, include, exclude)? {
let next_value = model.getattr(property_name_py)?;

// TODO fix include & exclude
let value = match self.return_ob_type {
Some(ref ob_type) => infer_to_python_known(ob_type, next_value, next_include, next_exclude, extra),
None => infer_to_python(next_value, next_include, next_exclude, extra),
}?;
output_dict.set_item(self.alias_py.as_ref(py), value)?;
let key = match extra.by_alias {
true => self.alias_py.as_ref(py),
false => property_name_py,
};
output_dict.set_item(key, value)?;
}
Ok(())
}
Expand All @@ -136,8 +146,8 @@ pub(crate) struct ComputedFieldSerializer<'py> {
impl<'py> Serialize for ComputedFieldSerializer<'py> {
fn serialize<S: serde::ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let py = self.model.py();
let property_name = self.computed_field.property_name.as_ref(py);
let next_value = self.model.getattr(property_name).map_err(py_err_se_err)?;
let property_name_py = self.computed_field.property_name_py.as_ref(py);
let next_value = self.model.getattr(property_name_py).map_err(py_err_se_err)?;

match self.computed_field.return_ob_type {
Some(ref ob_type) => {
Expand Down
5 changes: 3 additions & 2 deletions src/serializers/type_serializers/typed_dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,11 @@ impl BuildSerializer for TypedDictSerializer {
let mut exclude: Vec<Py<PyString>> = Vec::with_capacity(fields_dict.len());

for (key, value) in fields_dict.iter() {
let key: String = key.extract()?;
let key_py: &PyString = key.downcast()?;
let key: String = key_py.extract()?;
let field_info: &PyDict = value.downcast()?;

let key_py: Py<PyString> = PyString::intern(py, &key).into_py(py);
let key_py: Py<PyString> = key_py.into_py(py);

if field_info.get_as(intern!(py, "serialization_exclude"))? == Some(true) {
exclude.push(key_py.clone_ref(py));
Expand Down
49 changes: 49 additions & 0 deletions tests/serializers/test_model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import dataclasses
import json
import platform
from random import randint
from typing import Any, ClassVar

try:
Expand Down Expand Up @@ -694,3 +695,51 @@ def b(self):
assert s.to_json(Model(1), exclude={'b'}) == b'{"a":1}'
assert s.to_json(Model(1), include={'a'}) == b'{"a":1}'
assert s.to_json(Model(1), exclude={'b': [0]}) == b'{"a":1,"b":[2,"3"]}'


def test_property_setter():
class Square:
side: float

def __init__(self, **kwargs):
self.__dict__ = kwargs

@property
def area(self) -> float:
return self.side**2

@area.setter
def area(self, area: float) -> None:
self.side = area**0.5

@area.deleter
def area(self) -> None:
self.side = 0.0

@cached_property
def random_n(self) -> int:
return randint(0, 1_000)

s = SchemaSerializer(
core_schema.model_schema(
Square,
core_schema.typed_dict_schema(
{'side': core_schema.typed_dict_field(core_schema.float_schema())},
computed_fields=[
core_schema.computed_field('area', json_return_type='float'),
core_schema.computed_field('random_n', alias='The random number', json_return_type='int'),
],
),
)
)

sq = Square(side=10.0)
the_random_n = sq.random_n
assert s.to_python(sq, by_alias=True) == {'side': 10.0, 'area': 100.0, 'The random number': the_random_n}
assert s.to_json(sq, by_alias=True) == b'{"side":10.0,"area":100.0,"The random number":%d}' % the_random_n
sq.area = 49.0
assert s.to_python(sq, by_alias=False) == {'side': 7, 'area': 49, 'random_n': the_random_n}
assert s.to_json(sq, by_alias=False) == b'{"side":7.0,"area":49.0,"random_n":%d}' % the_random_n
del sq.area
assert s.to_python(sq, by_alias=False) == {'side': 0, 'area': 0, 'random_n': the_random_n}
assert s.to_python(sq, exclude={'random_n'}) == {'side': 0, 'area': 0}

0 comments on commit 0b1859c

Please sign in to comment.