Skip to content

Commit

Permalink
Improve ModelSerializer #66
Browse files Browse the repository at this point in the history
  • Loading branch information
AliRn76 committed Jan 29, 2024
2 parents 8935903 + 4743554 commit c61f41f
Show file tree
Hide file tree
Showing 8 changed files with 553 additions and 271 deletions.
3 changes: 3 additions & 0 deletions docs/docs/release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
### 3.9.0
- Change the style of `ModelSerializer` usage

### 3.8.2
- Add `content-type = application/json` header in raise response of `__call__`

Expand Down
150 changes: 98 additions & 52 deletions docs/docs/serializer.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,68 +5,114 @@ You can write your `serializer` in 2 style:
Write a normal `pydantic` class and use it as serializer:

```python
from pydantic import BaseModel
from pydantic import Field

from panther.app import API
from panther.request import Request
from panther.response import Response


class UserSerializer(BaseModel):
username: str
password: str
first_name: str = Field(default='', min_length=2)
last_name: str = Field(default='', min_length=4)


@API(input_model=UserSerializer)
async def serializer_example(request: Request):
return Response(data=request.validated_data)
from pydantic import BaseModel
from pydantic import Field
from panther.app import API
from panther.request import Request
from panther.response import Response
class UserSerializer(BaseModel):
username: str
password: str
first_name: str = Field(default='', min_length=2)
last_name: str = Field(default='', min_length=4)
@API(input_model=UserSerializer)
async def serializer_example(request: Request):
return Response(data=request.validated_data)
```

## Style 2 (Model Serializer)
Use panther `ModelSerializer` to write your serializer which will use your `model` fields as its fields, and you can say which fields are `required`
### Simple Usage

Use panther `ModelSerializer` to write your serializer which will use your `model` to create fields.

```python
from pydantic import Field

from panther import status
from panther.app import API
from panther.db import Model
from panther.request import Request
from panther.response import Response
from panther.serializer import ModelSerializer


class User(Model):
username: str
password: str
first_name: str = Field(default='', min_length=2)
last_name: str = Field(default='', min_length=4)


class UserModelSerializer(metaclass=ModelSerializer, model=User):
fields = ['username', 'first_name', 'last_name']
required_fields = ['first_name']


@API(input_model=UserModelSerializer)
async def model_serializer_example(request: Request):
return Response(data=request.validated_data, status_code=status.HTTP_202_ACCEPTED)
from pydantic import Field

from panther import status
from panther.app import API
from panther.db import Model
from panther.request import Request
from panther.response import Response
from panther.serializer import ModelSerializer


class User(Model):
username: str
password: str
first_name: str = Field(default='', min_length=2)
last_name: str = Field(default='', min_length=4)


class UserModelSerializer(ModelSerializer):
class Config:
model = User
fields = ['username', 'first_name', 'last_name']
required_fields = ['first_name']


@API(input_model=UserModelSerializer)
async def model_serializer_example(request: Request):
return Response(data=request.validated_data, status_code=status.HTTP_202_ACCEPTED)
```

## Notes
1. In the example above `UserModelSerializer` only accepts the values of `fields` attribute
### Notes
1. In the example above, `ModelSerializer` will look up for the value of `Config.fields` in the `User.fields` and use their `type` and `value` for the `validation`.
2. `Config.model` and `Config.fields` are `required` when you are using `ModelSerializer`.
3. If you want to use `Config.required_fields`, you have to put its value in `Config.fields` too.



### Complex Usage

You can use `pydantic.BaseModel` features in `ModelSerializer` too.

```python
from pydantic import Field, field_validator, ConfigDict

2. In default the `UserModelSerializer.fields` are same as `User.fields` but you can change their default and make them required with `required_fields` attribute
from panther import status
from panther.app import API
from panther.db import Model
from panther.request import Request
from panther.response import Response
from panther.serializer import ModelSerializer


class User(Model):
username: str
password: str
first_name: str = Field(default='', min_length=2)
last_name: str = Field(default='', min_length=4)

3. If you want to use `required_fields` you have to put them in `fields` too.
class UserModelSerializer(ModelSerializer):
model_config = ConfigDict(str_to_upper=True)
age: int = Field(default=20)
is_male: bool
username: str

class Config:
model = User
fields = ['username', 'first_name', 'last_name']
required_fields = ['first_name']

4. `fields` attribute is `required` when you are using `ModelSerializer` as `metaclass`
@field_validator('username')
def validate_username(cls, username):
print(f'{username=}')
return username

5. `model=` is required when you are using `ModelSerializer` as `metaclass`

@API(input_model=UserModelSerializer)
async def model_serializer_example(request: Request):
return Response(data=request.validated_data, status_code=status.HTTP_202_ACCEPTED)
```

6. You have to use `ModelSerializer` as `metaclass` (not as a parent)
### Notes
1. You can add custom `fields` in `pydantic style`
2. You can add `model_config` as `attribute` and also in the `Config`
3. You can use `@field_validator` and other `validators` of `pydantic`.


7. Panther is going to create a `pydantic` model as your `UserModelSerializer` in the startup
39 changes: 24 additions & 15 deletions example/model_serializer_example.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
from pydantic import Field
from pydantic import Field, field_validator, ConfigDict

from panther import Panther, status
from panther.app import API
from panther.db import Model
from panther.request import Request
from panther.response import Response
from panther.serializer import ModelSerializer


Expand All @@ -15,18 +11,31 @@ class User(Model):
last_name: str = Field(default='', min_length=4)


class UserSerializer(metaclass=ModelSerializer, model=User):
fields = ['username', 'first_name', 'last_name']
# required_fields = ['first_name']
class UserSerializer(ModelSerializer):
"""
Hello this is doc
"""
model_config = ConfigDict(str_to_upper=False) # Has more priority
age: int = Field(default=20)
is_male: bool
username: str

class Config:
str_to_upper = True
model = User
fields = ['username', 'first_name', 'last_name']
required_fields = ['first_name']

@API(input_model=UserSerializer)
async def model_serializer_example(request: Request):
return Response(data=request.validated_data, status_code=status.HTTP_202_ACCEPTED)
@field_validator('username', mode='before')
def validate_username(cls, username):
print(f'{username=}')
return username

def create(self) -> type[Model]:
print('UserSerializer.create()')
return super().create()

url_routing = {
'': model_serializer_example,
}

app = Panther(__name__, configs=__name__, urls=url_routing)
serialized = UserSerializer(username='alirn', first_name='Ali', last_name='RnRn', is_male=1)
print(serialized)
# serialized.create()
2 changes: 1 addition & 1 deletion panther/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from panther.main import Panther # noqa: F401

__version__ = '3.8.2'
__version__ = '3.9.0'


def version():
Expand Down
2 changes: 2 additions & 0 deletions panther/cli/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,15 @@ async def info_api(request: Request):

TEMPLATE = {
'app': {
'__init__.py': '',
'apis.py': apis_py,
'models.py': models_py,
'serializers.py': serializers_py,
'throttling.py': throttling_py,
'urls.py': app_urls_py,
},
'core': {
'__init__.py': '',
'configs.py': configs_py,
'urls.py': urls_py,
},
Expand Down

0 comments on commit c61f41f

Please sign in to comment.