# Python data model examples

Data models with dataclasses - type-safe requests and responses, serialized to JSON and OpenAPI automatically.

- Building a data model for your API services revolves around the usage of Python's built-in [dataclasses](https://zato.io/docs/dev/model/index.html).
- Dataclasses allow you to define your models in a declarative way that lets you integrate them with your IDE for type-completion or type checking.
- Furthermore, Zato extends dataclasses to support auto-serialization to and from Python dicts, JSON and [OpenAPI](https://zato.io/docs/dev/rest/openapi.html) definitions.
- Just like the services, models can be hot-deployed to Zato servers without any restart
- Models are reusable across services or systems - they are not specific to REST only
- Input can also be declared without a model, as a tuple of field names - [declaring input and output](https://zato.io/docs/dev/input-output.html) shows both forms side by side

## Typical usage {#typical-usage}

- Each request and response object is a dataclass subclassing a `Model` class
- In your code, you work only with high-level business objects such as `GetClientRequest` and `GetClientResponse` below
- Zato automatically serializes the objects from and to JSON, Python dicts or OpenAPI definitions

```python
# -*- coding: utf-8 -*-

# stdlib
from dataclasses import dataclass

# Zato
from zato.server.service import Model, Service

# ###########################################################################

@dataclass(init=False)
class GetClientRequest(Model):
    client_id: int

@dataclass(init=False)
class GetClientResponse(Model):
    name:    str
    email:   str
    segment: str

# ###########################################################################

class GetClient(Service):

    input  = GetClientRequest
    output = GetClientResponse

    def handle(self):

        # Enable type checking and type completion
        request = self.request.input # type: GetClientRequest

        # Log details of our request
        self.logger.info('Processing client `%s`', request.client_id)

        # Produce our response - in a full service this information
        # will be obtained from one or more databases or systems.
        response = GetClientResponse()
        response.name    = 'Jane Doe'
        response.email   = 'hello@example.com'
        response.segment = 'RNZ'

        # Return the response to our caller
        self.response.payload = response

# ###########################################################################
```

Let's invoke it:

```text
$ curl http://pubapi:<password>@localhost:11223/zato/api/invoke/client.get-client \
    -d '{"client_id":123}' ; echo
{"name":"Jane Doe","email":"hello@example.com","segment":"RNZ"}
$
```

## Working with lists {#working-with-lists}

- In the example below, we return a list of objects
- Dataclasses can be nested in other dataclasses, forming arbitrary hierarchies of objects in a data model

```python
# -*- coding: utf-8 -*-

# stdlib
from dataclasses import dataclass

# Zato
from zato.common.typing_ import list_
from zato.server.service import Model, Service

# ###########################################################################

@dataclass(init=False)
class Phone(Model):
    imei:       str
    owner_id:   int
    owner_name: str

# ###########################################################################

@dataclass(init=False)
class GetPhoneListRequest(Model):
    client_id: int

@dataclass(init=False)
class GetPhoneListResponse(Model):
    phone_list:    list_[Phone]
    response_type: str

# ###########################################################################

class GetPhoneDetails(Service):

    input  = GetPhoneListRequest
    output = GetPhoneListResponse

    def handle(self):

        # Enable type checking and type completion
        request = self.request.input # type: GetPhoneListRequest

        # Log details of our request
        self.logger.info('Processing client `%s`', request.client_id)

        # Build our response now - in a full service this information
        # would be read from an exteran system or database.

        # Our list of phones to return
        phone_list = []

        # Build the fist phone ..
        phone1 = Phone()
        phone1.imei = '123'
        phone1.owner_id = 456
        phone1.owner_name = 'John Doe'

        # .. the second one ..
        phone2 = Phone()
        phone2.imei = '789'
        phone2.owner_id = 999
        phone2.owner_name = 'Jane Doe'

        # .. populate the container for phones tha we return ..
        phone_list.append(phone1)
        phone_list.append(phone2)

        # .. build the top-level response element ..
        response = GetPhoneListResponse()
        response.response_type = 'RZH'
        response.phone_list = phone_list

        # .. and return the response to our caller
        self.response.payload = response

# ###########################################################################
```

We can invoke it now with:

```text
$ curl http://pubapi:<password>@localhost:11223/zato/api/invoke/phone.get-phone-list \
    -d '{"client_id":789}'
{"response_type":"RZH",
 "phone_list": [
    {"imei":"123","owner_id":456,"owner_name":"John Doe"},
    {"imei":"789","owner_id":999,"owner_name":"Jane Doe"}]}
$
```

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - The complete reference for writing services
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how both are structured
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
