Skip to main content

A troubling rest api library for sqlalchemy models

Project description

UnRest - <small>Simple sqlalchemy rest api generation.</small>

Build Status Coverage Status

from unrest import UnRest
# First, initialize UnRest with your web application
rest = UnRest(app)

# Then declare your endpoint
rest(Person)

This should provide you a /api/person and a /api/person/<login> route accessible in GET only.

To activate data modification, set the methods array like this:

rest(Person, only=['name', 'sex', 'age'], methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH']) # or simply methods=rest.all

You will get both routes (collection and member) on the five methods. Please see the wikipedia page for their signification.

You can also override the default methods like this:

person = rest(Person)

@person.declare('GET')
def get(payload, login=None):
    # Pre get hook
    if login:
        login = login.upper()
    rv = person.get(payload, login=login)
    # Post get hook
    return {
        'occurences': rv['occurences'],
        'objects': [
            {'login': person['login'].lower()} for person in rv['objects']
        ]
    }

Documentation

Full documentation can be found at kozea.github.io/unrest

Example

Consider this simple web application:

from datetime import timedelta

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import relationship
from sqlalchemy.sql.schema import Column, ForeignKey
from sqlalchemy.types import Integer, Interval, Numeric, String

from unrest import UnRest

# Flask application
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/unrest.db'

# Model definition
db = SQLAlchemy(app)

class Tree(db.Model):
    id = Column(Integer, primary_key=True)
    name = Column(String)

    @property
    def fruit_colors(self):
        return ', '.join([fruit.color for fruit in self.fruits])

class Fruit(db.Model):
    fruit_id = Column(Integer, primary_key=True)
    color = Column(String(50))
    size = Column(Numeric)
    age = Column(Interval)
    tree_id = Column(Integer, ForeignKey('tree.id'))
    tree = relationship(Tree, backref='fruits')

# Drop everything just in case
db.drop_all()

# Create model
db.create_all()

# Data insertion
pine = Tree(name='pine')
maple = Tree(name='maple')
oak = Tree(name='oak')
db.session.add(pine)
db.session.add(maple)
db.session.add(oak)

db.session.add(Fruit(color='grey', size=12, age=timedelta(days=12, hours=1, minutes=15), tree=pine))
db.session.add(Fruit(color='darkgrey', size=23, age=timedelta(days=49, seconds=230, milliseconds=213), tree=pine))
db.session.add(Fruit(color='brown', size=2.12, age=timedelta(0), tree=pine))
db.session.add(Fruit(color='red', size=.5, age=timedelta(minutes=40), tree=maple))
db.session.add(Fruit(color='orangered', size=100, age=timedelta(hours=2, microseconds=12), tree=maple))

db.session.commit()
db.session.remove()

# Declare rest endpoints
rest = UnRest(app, db.session)

# Authorize every methods
rest(Tree, methods=rest.all, allow_batch=True)
# Don't authorize batch for fruits
rest(Fruit, methods=rest.all)

# Run the app
app.run(debug=True)

You will now have:

GET

With primary keys arguments

$ curl -s http://localhost:5000/api/tree/1

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "id": 1,
            "name": "pine"
        }
    ]
}
$ curl -s http://localhost:5000/api/fruit/1

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "fruit_id": 1,
            "color": "grey",
            "size": 12.0,
            "age": 1041300.0,
            "tree_id": 1
        }
    ]
}

Without argument

$ curl -s http://localhost:5000/api/tree

200 OK
{
    "occurences": 3,
    "objects": [
        {
            "id": 1,
            "name": "pine"
        },
        {
            "id": 2,
            "name": "maple"
        },
        {
            "id": 3,
            "name": "oak"
        }
    ]
}
$ curl -s http://localhost:5000/api/fruit

200 OK
{
    "occurences": 5,
    "objects": [
        {
            "fruit_id": 1,
            "color": "grey",
            "size": 12.0,
            "age": 1041300.0,
            "tree_id": 1
        },
        {
            "fruit_id": 2,
            "color": "darkgrey",
            "size": 23.0,
            "age": 4233830.213,
            "tree_id": 1
        },
        {
            "fruit_id": 3,
            "color": "brown",
            "size": 2.12,
            "age": 0.0,
            "tree_id": 1
        },
        {
            "fruit_id": 4,
            "color": "red",
            "size": 0.5,
            "age": 2400.0,
            "tree_id": 2
        },
        {
            "fruit_id": 5,
            "color": "orangered",
            "size": 100.0,
            "age": 7200.000012,
            "tree_id": 2
        }
    ]
}

PUT

With primary keys arguments

$ curl -s http://localhost:5000/api/tree/1 -X PUT -H "Content-Type: application/json" -d '{
  "name": "cedar"
}'

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "id": 1,
            "name": "cedar"
        }
    ]
}

Get it again to be sure:

$ curl -s http://localhost:5000/api/tree/1

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "id": 1,
            "name": "cedar"
        }
    ]
}

Without argument

$ curl -s http://localhost:5000/api/tree -X PUT -H "Content-Type: application/json" -d '{
  "objects": [{"id": 2, "name": "cedar"}, {"id": 22, "name": "mango"}]
}'

200 OK
{
    "occurences": 2,
    "objects": [
        {
            "id": 2,
            "name": "cedar"
        },
        {
            "id": 22,
            "name": "mango"
        }
    ]
}

Get it again to be sure:

$ curl -s http://localhost:5000/api/tree

200 OK
{
    "occurences": 2,
    "objects": [
        {
            "id": 2,
            "name": "cedar"
        },
        {
            "id": 22,
            "name": "mango"
        }
    ]
}

Check that when allow_batch is not set we can't put all:

$ curl -s http://localhost:5000/api/fruit -X PUT -H "Content-Type: application/json" -d '{
  "objects": [
    {"fruit_id": 2, "color": "red"},
    {"fruit_id": 22, "color": "blue"}
  ]
}'

406 Not Acceptable
{
  "message": "You must set allow_batch to True if you want to use batch methods."
}

POST

With primary keys arguments

$ curl -s http://localhost:5000/api/tree/1 -X POST -H "Content-Type: application/json"

501 Not Implemented
{
  "message": "POST on id corresponds to collection creation. It's not implemented by default. If you want to update an item use the PUT method instead"
}

Without argument

$ curl -s http://localhost:5000/api/fruit -X POST -H "Content-Type: application/json" -d '{
  "color": "forestgreen", "size": 3.14, "age": 1.5926, "tree_id": 3
}'

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "fruit_id": 6,
            "color": "forestgreen",
            "size": 3.14,
            "age": 1.5926,
            "tree_id": 3
        }
    ]
}

Now we should have a total of 6 fruits:

$ curl -s http://localhost:5000/api/fruit

200 OK
{
    "occurences": 6,
    "objects": [
        {
            "fruit_id": 1,
            "color": "grey",
            "size": 12.0,
            "age": 1041300.0,
            "tree_id": 1
        },
        {
            "fruit_id": 2,
            "color": "darkgrey",
            "size": 23.0,
            "age": 4233830.213,
            "tree_id": 1
        },
        {
            "fruit_id": 3,
            "color": "brown",
            "size": 2.12,
            "age": 0.0,
            "tree_id": 1
        },
        {
            "fruit_id": 4,
            "color": "red",
            "size": 0.5,
            "age": 2400.0,
            "tree_id": 2
        },
        {
            "fruit_id": 5,
            "color": "orangered",
            "size": 100.0,
            "age": 7200.000012,
            "tree_id": 2
        },
        {
            "fruit_id": 6,
            "color": "forestgreen",
            "size": 3.14,
            "age": 1.5926,
            "tree_id": 3
        }
    ]
}

DELETE

With primary keys arguments

$ curl -s http://localhost:5000/api/fruit/3 -X DELETE

200 OK
{
    "occurences": 1,
    "objects": [
        {
            "fruit_id": 3,
            "color": "brown",
            "size": 2.12,
            "age": 0.0,
            "tree_id": 1
        }
    ]
}

Now we should have only 5 fruits remaining:

$ curl -s http://localhost:5000/api/fruit

200 OK
{
    "occurences": 5,
    "objects": [
        {
            "fruit_id": 1,
            "color": "grey",
            "size": 12.0,
            "age": 1041300.0,
            "tree_id": 1
        },
        {
            "fruit_id": 2,
            "color": "darkgrey",
            "size": 23.0,
            "age": 4233830.213,
            "tree_id": 1
        },
        {
            "fruit_id": 4,
            "color": "red",
            "size": 0.5,
            "age": 2400.0,
            "tree_id": 2
        },
        {
            "fruit_id": 5,
            "color": "orangered",
            "size": 100.0,
            "age": 7200.000012,
            "tree_id": 2
        },
        {
            "fruit_id": 6,
            "color": "forestgreen",
            "size": 3.14,
            "age": 1.5926,
            "tree_id": 3
        }
    ]
}

Without argument

Batch delete is not allowed on fruit:

$ curl -s http://localhost:5000/api/fruit -X DELETE

501 Not Implemented
{
  "message": "You must set allow_batch to True if you want to use batch methods."
}

But is on tree:

$ curl -s http://localhost:5000/api/tree -X DELETE

200 OK
{
    "occurences": 3,
    "objects": [
        {
            "id": 1,
            "name": "pine"
        },
        {
            "id": 2,
            "name": "maple"
        },
        {
            "id": 3,
            "name": "oak"
        }
    ]
}
$ curl -s http://localhost:5000/api/tree

200 OK
{
    "occurences": 0,
    "objects": []
}

PATCH

With primary keys arguments

$ curl -s http://localhost:5000/api/fruit/1 -X PATCH -H "Content-Type: application/json" -d '{
  "color": "blue"
}'

200 OK
{
    "occurences": 1,
    "objects": [
        {
          "fruit_id": 1,
          "color": "blue",
          "size": 12.0,
          "age": 1041300.0,
          "tree_id": 1
        }
    ]
}

Without argument

$ curl -s http://localhost:5000/api/tree -X PATCH -H "Content-Type: application/json" -d '{
  "objects": [{"id": 2, "name": "cedar"}, {"id": 3, "name": "mango"}]
}'

200 OK
{
    "occurences": 2,
    "objects": [
        {
            "id": 2,
            "name": "cedar"
        },
        {
            "id": 3,
            "name": "mango"
        }
    ]
}

Check that when allow_batch is not set we can't put all:

$ curl -s http://localhost:5000/api/fruit -X PATCH -H "Content-Type: application/json" -d '{
  "objects": [
    {"fruit_id": 1, "color": "blue"},
    {"fruit_id": 3, "age": 1038540.0},
    {"fruit_id": 4, "color": "rainbow", "size": 8},
    {"fruit_id": 5, "size": 10, "tree_id": 1}
  ]
}'

406 Not Acceptable
{
  "message": "You must set allow_batch to True if you want to use batch methods."
}

otherwise all the specified attributes would have been patched.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

unrest-1.1.0.tar.gz (43.9 kB view hashes)

Uploaded source

Built Distribution

unrest-1.1.0-py3-none-any.whl (57.6 kB view hashes)

Uploaded py3

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page