Skip to main content

UnRest - Simple sqlalchemy rest api generation.

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.

Metadata

Release files for unrest 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for unrest 1.1.0
File Size Uploaded Provenance
unrest-1.1.0.tar.gz 43.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for unrest 1.1.0
File Interpreter ABI Platform
unrest-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 101.5 kB

Release files / unrest-1.1.0.tar.gz

Download URL unrest-1.1.0.tar.gz
Size 43.9 kB
Tags Source
SHA-256 checksum
How to use checksums
be0417c08b0675698641258ab5f1e921c71ba8d6c3b54283566ebec7a7762164
BLAKE2b-256 checksum
How to use checksums
d9fc0b9ce1c8018752824c2d72f3988f190cf07d1d53527e9c6811ce7e7e9374
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.25.1

Release files / unrest-1.1.0-py3-none-any.whl

Download URL unrest-1.1.0-py3-none-any.whl
Size 57.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0c2ee6294f4f3d7c94c01aa3f56c51ad70e8a8e5f68b6efdcc37d185360efbf6
BLAKE2b-256 checksum
How to use checksums
824c66f47280d043b8700541de96d4c170eb147eeb9c5cd59e0c46a9839ae0c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.25.1

Security for unrest 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page