Skip to content

Commit

Permalink
Merge pull request #1 from vinyguedess/stage
Browse files Browse the repository at this point in the history
v2021.11.07
  • Loading branch information
vinyguedess authored Nov 7, 2021
2 parents cc7e918 + 940604b commit bfb9219
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 143 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
name: Deploy

on:
- release
release:
types: [ created ]

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
pull_request:
branches: [ master ]
branches: [ master, stage ]

jobs:
build:
Expand Down
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,10 @@ deploy:
python setup.py sdist bdist_wheel
twine upload dist/*

doc:
rm -rf README.rst
m2r README.md

lint:
black alcherializer tests
isort alcherializer tests

test:
python -m pytest -p no:warnings --cov=alcherializer -v
python -m pytest -p no:warnings --cov=alcherializer -vv
coverage html
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ This instantly maps all fields declared in model.
from datetime import datetime
from alcherializer import Serializer
import sqlalchemy
from sqlalchemy.orm import relationship


class Manager:
name = sqlalchemy.Column(sqlalchemy.String(100))


class User:
name = sqlalchemy.Column(sqlalchemy.String(100))
age = sqlalchemy.Column(sqlalchemy.Integer)
is_active = sqlalchemy.Column(sqlalchemy.Boolean)
created_at = sqlalchemy.Column(sqlalchemy.DateTime, default=datetime.utcnow)

organization = relationship(Manager, uselist=False)


class UserSerializer(Serializer):
Expand Down Expand Up @@ -53,6 +60,52 @@ serializer = UserSerializer([model], many=True)
serializer.data # [{ "name": "Clark Kent", ... }]
```

### Related Serializers
```python
class ManagerSerializer(Serializer):
class Meta:
model = Manager


class UserSerializer(Serializer):
manager = ManagerSerializer()

class Meta:
model = User


model = User(
name="Peter Parker",
manager=Manager(
name="J. Jonah Jameson"
)
)
serializer = UserSerializer(model)
serializer.data # {"name": "Peter Parker", "manager": {"name": "J. Jonah Jameson"}}
```

### Custom fields
```python
from datetime import datetime, timedelta
from alcherializer import fields

class UserSerializer(Serializer):
year_of_birth = fields.MethodField()

def get_year_of_birth(self, user: User) -> datetime:
return datetime.utcnow() - timedelta(days=user.age * 365)

class Meta:
model = User
fields = ["id", "name", "year_of_birth"]


model = User(id=1, name="Batman", age=30)

serializer = UserSerializer(model)
serializer.data # {"id": 1, "name": "Batman", "year_of_birth": 1991}
```

## Validation
To validate a payload, it's possible to send it through data argument while
instantiating the serializer and call **.is_valid** method.
Expand Down
132 changes: 0 additions & 132 deletions README.rst

This file was deleted.

4 changes: 4 additions & 0 deletions alcherializer/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,7 @@ def check_if_length_is_under_limit(
return False, f"Limit of characters is {self.field.type.length}"

return True, None


class MethodField(BaseField):
pass
53 changes: 50 additions & 3 deletions alcherializer/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
)

import sqlalchemy
from sqlalchemy.ext.declarative import DeclarativeMeta

from alcherializer import fields
from alcherializer.exceptions import MalformedMetaClassException
Expand Down Expand Up @@ -32,7 +33,7 @@ def data(self) -> Dict[str, Any]:
for instance in instances:
obj_dict = {}
for key in self.fields.keys():
obj_dict[key] = getattr(instance, key)
obj_dict[key] = self._get_instance_field_value(instance, key)

results.append(obj_dict)

Expand Down Expand Up @@ -64,17 +65,39 @@ def _has_errors(self) -> bool:
return len(self.errors.keys()) <= 0

def _get_fields(self) -> Dict[str, Any]:
required_fields = []
if hasattr(self.meta, "fields"):
required_fields = self.meta.fields

columns = {}
for key, value in self.meta.model.__dict__.items():
if key.startswith("_"):
continue

if required_fields and key not in required_fields:
continue

columns[key] = {
"type": value.type,
"required": value.nullable is False,
"type": value.type if hasattr(value, "type") else value,
"required": value.nullable is False
if hasattr(value, "nullable")
else False,
"validator": self._get_field_validator(key, value),
}

for field in required_fields:
if field in columns:
continue

if not hasattr(self, field):
continue

columns[field] = {
"type": getattr(self, field),
"required": False,
"validator": self._get_field_validator(field, field),
}

return columns

def _get_field_validator(self, key: str, field):
Expand All @@ -95,3 +118,27 @@ def _get_field_validator(self, key: str, field):
return fields.BooleanField(key, field)

return fields.BaseField(key, field)

def _get_instance_field_value(self, instance, field: str) -> Any:
if isinstance(self.fields[field].get("type"), fields.MethodField):
return getattr(self, f"get_{field}", lambda o: None)(instance)

value = getattr(instance, field)
if isinstance(value.__class__, DeclarativeMeta):
serializer = self.fields[field]["validator"]
if isinstance(serializer, Serializer):
serializer.instance = value
return serializer.data

if (
isinstance(value, list)
and len(value) > 0
and isinstance(value[0].__class__, DeclarativeMeta)
):
serializer = self.fields[field]["validator"]
if isinstance(serializer, Serializer):
serializer.instance = value
serializer.many = True
return serializer.data

return value
Loading

0 comments on commit bfb9219

Please sign in to comment.