skip to navigation
skip to content

Flask-RESTive 0.0.2

Flask RESTive is a REST API Flask extension based on Flask-RESTful & Marshmallow.

Latest Version: 0.0.3

# flask-restive
Flask-RESTive is a REST API Flask extension based on [Flask-RESTful](https://github.com/flask-restful/flask-restful) & [Marshmallow](https://github.com/marshmallow-code/marshmallow)

[![Build Status](https://travis-ci.org/left-join/flask-restive.svg?branch=master)](https://travis-ci.org/left-join/flask-restive)
[![Coverage Status](https://coveralls.io/repos/github/left-join/flask-restive/badge.svg?branch=master)](https://coveralls.io/github/left-join/flask-restive?branch=master)
[![Code Health](https://landscape.io/github/left-join/flask-restive/master/landscape.svg?style=flat)](https://landscape.io/github/left-join/flask-restive/master)
[![PyPI Version](https://img.shields.io/pypi/v/Flask-RESTive.svg)](https://pypi.python.org/pypi/Flask-RESTive)


## Installation
```bash
pip install flask-restive
```

## Requirements
- Python >= 2.7 or >= 3.4

## Introdution

#### Reusable resource concept
In many cases we don't need to duplicate resource's methods code.
Flask-RESTive adheres to a declarative approach. All that we need it's just define serializer behaviour and repo behaviour. The resource code it is not a place for define any business logic, it's view and we use it just for call serializers, repo and results render.
```python
class ClientResource(StorageResource):
data_schema_cls = ClientSchema
storage_cls = ClientStorage
```

#### Storage concept
Storage is a repo class in DDD (Domain Driven Design) methodology. Storage can implement workflow with any database or multiple databases. Abstract storage provides interface methods:
```python
def open(self):
...

def close(self, exception=None):
...

def get_item(self, filter_params, **kwargs):
...

def get_count(self, filter_params=None, **kwargs):
...

def get_list(self, filter_params=None, slice_params=None, sorting_params=None, **kwargs):
...

def create_item(self, data_params, **kwargs):
...

def create_list(self, data_params, **kwargs):
...

def update_item(self, data_params, **kwargs):
...

def update_list(self, data_params, **kwargs):
...

def delete_list(self, filter_params=None, **kwargs):
...
```
Anybody can make his own implementation of his special storage. Combine simple storage bricks to implement business logic layer in your storage.
Storage supports **primary_key_fields** meta-attribute and use it to wrap result data to special object with primary_key property.
```python
class ClientStorage(Storage):
class Meta(Storage.Meta):
primary_key_fields = ('id',)
```
Wrapped objects are more useful to work with them on many storage combining and result processing.

#### Schema concept
Schema is a Marshmallow library class that implements serializer/deserializer logic. It's useful to define model fields in declarative style. It's a right to place to make any data validations or transmutations before or after storage data processing.
```python
class ClientSchema(Schema):
id = fields.Integer(required=True)
first_name = fields.String(required=True)
last_name = fields.String()
```
Data schema supports **primary_key_fields**, **sortable_fields** and **default_sorting** meta-attributes. Filter schema and sorting schema use it to auto-make filter and sorting fields and validation rules.
```python
class ClientSchema(Schema):
id = fields.Integer(required=True)
first_name = fields.String(required=True)
last_name = fields.String()

class Meta(Schema.Meta):
sortable_fields = ('id', 'first_name', 'last_name')
default_sorting = ('last_name', 'first_name', 'id')
```

## How to use

```python
from datetime import datetime

from flask import Flask
from flask_restive import Api, StorageResource, UUIDSchema, fields
from marshmallow import pre_load
from flask_restive_sqlalchemy import Model, Storage
from sqlalchemy import Column, String, DateTime
from sqlalchemy_utils import UUIDType


app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'


def utc_time():
return datetime.utcnow().replace(microsecond=0)


class ClientSchema(UUIDSchema):
first_name = fields.String(required=True)
last_name = fields.String(required=True)
created_on = fields.DateTime(
required=True,
missing=lambda: utc_time().isoformat())
updated_on = fields.DateTime()

class Meta(UUIDSchema.Meta):
sortable_fields = ('id', 'created_on', 'updated_on')
default_sorting = ('-updated_on', '-created_on', 'id')

@pre_load(pass_many=False)
def set_updated_on(self, data):
# update time stamp on each create/update operation
data['updated_on'] = utc_time().isoformat()
return data


class ClientModel(Model):
id = Column(UUIDType, primary_key=True)
first_name = Column(String)
last_name = Column(String)
created_on = Column(DateTime)
updated_on = Column(DateTime)


class ClientStorage(Storage):

class Meta(Storage.Meta):
model_cls = ClientModel
primary_key_fields = ('id',)


class ClientResource(StorageResource):
data_schema_cls = ClientSchema
storage_cls = ClientStorage


api = Api(app, prefix='/api/v1', api_resources=[
(ClientResource, ('/clients', '/clients/<uuid:id>')),
])


if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)

```

Let's create new client:
```bash
curl -X POST "http://localhost:5000/api/v1/clients" -H "Content-Type: application/json" -d '{"first_name": "Alice", "last_name": "Liddell"}'
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice", "last_name": "Liddell",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:44:37"
}
```

Let's create two more:
```bash
curl -X POST "http://localhost:5000/api/v1/clients" -H "Content-Type: application/json" -d '[{"first_name": "Mad", "last_name": "Hatter"}, {"first_name": "Cheshire", "last_name": "Cat"}]'
[
{
"id": "a593f5e2-e588-4e2a-ae57-c4dd8a3faed5",
"first_name": "Mad",
"last_name": "Hatter",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "c761ef71-d4b0-4b14-aa45-549ffcb72234",
"first_name": "Cheshire",
"last_name": "Cat",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
}
]
```

Let's list created clients:
```bash
curl -X GET "http://localhost:5000/api/v1/clients"
{
"offset": 0,
"limit": null,
"total_count": 3,
"items_count": 3,
"items_list": [
{
"id": "a593f5e2-e588-4e2a-ae57-c4dd8a3faed5",
"first_name": "Mad",
"last_name": "Hatter",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "c761ef71-d4b0-4b14-aa45-549ffcb72234",
"first_name": "Cheshire",
"last_name": "Cat", "created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Liddell",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:44:37"
}
]
}
```

Let's take one client:
```bash
curl -X GET "http://localhost:5000/api/v1/clients/0372be43-a668-421e-b8df-7246cdb40857"
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Liddell",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:44:37"
}
```

Let's paginate list of clients:
```bash
curl -X GET "http://localhost:5000/api/v1/clients?offset=2&limit=2"
{
"offset": 2,
"limit": 2,
"total_count": 3,
"items_count": 1,
"items_list": [
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Liddell",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:44:37"
}
]
}
```

Let's update one client:
```bash
curl -X PATCH "http://localhost:5000/api/v1/clients/0372be43-a668-421e-b8df-7246cdb40857" -H "Content-Type: application/json" -d '{"last_name": "Hatter"}'
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
}
```

Let's list clients again:
```bash
curl -X GET "http://localhost:5000/api/v1/clients"
{
"offset": 0,
"limit": null,
"total_count": 3,
"items_count": 3,
"items_list": [
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
},
{
"id": "a593f5e2-e588-4e2a-ae57-c4dd8a3faed5",
"first_name": "Mad",
"last_name": "Hatter",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "c761ef71-d4b0-4b14-aa45-549ffcb72234",
"first_name": "Cheshire",
"last_name": "Cat",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
}
]
}
```

Let's change sorting order:
```bash
curl -X GET "http://localhost:5000/api/v1/clients?sort_by=updated_on,created_on,-id"
{
"offset": 0,
"limit": null,
"total_count": 3,
"items_count": 3,
"items_list": [
{
"id": "c761ef71-d4b0-4b14-aa45-549ffcb72234",
"first_name": "Cheshire",
"last_name": "Cat",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "a593f5e2-e588-4e2a-ae57-c4dd8a3faed5",
"first_name": "Mad",
"last_name": "Hatter",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
},
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
}
]
}
```

Let's filter clients:
```bash
curl -X GET "http://localhost:5000/api/v1/clients?last_name=Hatter"
{
"offset": 0,
"limit": null,
"total_count": 2,
"items_count": 2,
"items_list": [
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
},
{
"id": "a593f5e2-e588-4e2a-ae57-c4dd8a3faed5",
"first_name": "Mad",
"last_name": "Hatter",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
}
]
}

Let's filter clients by date range:
```bash
curl -X GET "http://localhost:5000/api/v1/clients?created_on__min=2017-09-08T20:00:00&created_on__max=2017-09-08T20:45:00"
{
"offset": 0,
"limit": null,
"total_count": 1,
"items_count": 1,
"items_list": [
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
}
]
}
```

Let's filter clients by list of id:
```bash
curl -X GET "http://localhost:5000/api/v1/clients?id__in=0372be43-a668-421e-b8df-7246cdb40857,c761ef71-d4b0-4b14-aa45-549ffcb72234"
{
"offset": 0,
"limit": null,
"total_count": 2,
"items_count": 2,
"items_list": [
{
"id": "0372be43-a668-421e-b8df-7246cdb40857",
"first_name": "Alice",
"last_name": "Hatter",
"created_on": "2017-09-08T20:44:37",
"updated_on": "2017-09-08T20:52:07"
},
{
"id": "c761ef71-d4b0-4b14-aa45-549ffcb72234",
"first_name": "Cheshire",
"last_name": "Cat",
"created_on": "2017-09-08T20:45:15",
"updated_on": "2017-09-08T20:45:15"
}
]
}
```


 
File Type Py Version Uploaded on Size
Flask-RESTive-0.0.2.tar.gz (md5) Source 2017-09-13 19KB
Flask_RESTive-0.0.2-py2-none-any.whl (md5) Python Wheel py2 2017-09-13 25KB
Flask_RESTive-0.0.2-py3-none-any.whl (md5) Python Wheel py3 2017-09-13 25KB