Skip to main content

CRUD/RPC client over flask-restless

Project description

flask-restless-client

Build Status Codecov PyPI

Intoduction

The flask-restless-client is the second part of a two part library. The first being the flask-restless-datamodel. Together with the flask-restless-datamodel, this library serves as a goal to provide a CRUD/RPC client for Flask/SQLAlchemy applications, over HTTP using flask-restless.

Taking advantage of the easy integration offered by Flask-Restless to expose a REST CRUD interface over HTTP, this library uses its power to provide a dynamic python client. Reading the data format generated by the flask-restless-datamodel, the restless-client is able to build itself and provide you with objects that aim to mirror an SQLAlchemy-like interface on the client side.

This includes RPC possibilities to run object methods of the SQLA models defined server-side. Some setup is required to achieve a smooth interaction with the RPC part of this library.

The developer will be required to:

  • write serializers from and to python natives to transfer complex objects to the server
  • overwrite authentication method if the chosen authentication method is not supported by this library.

As such, it's advised to use this library as a base for a custom client for your application. Most likely, it will be a thin layer on top of the flask-restless-client setting up some configuration.

Installation

    pip install flask-restless-client

Setting up the client

Exposing your model server side

The first step is to enable the flask-restless-datamodel on the server side. You can visit flask-restless-datamodel to see how to do this.

Authenticating

As this library is intented to be useable out of the box, some built in authentication is provided. Current out of the box authentication types are Bearer and Basic Authentication.

By default, the client will use the Bearer session, but the Basic Authentication session is importable from restless_client.ext.auth.

You are also able to give your own (pre-authenticated) session as a parameter when initializing the client.

Environment variables can be set to speed up authentication setup. Using the prefix RESTLESS_CLIENT_ you can set anything involving authentication, including setting which type of session to use.

(De)Serialization of complex objects

Part of supporting an RPC-like client is making sure the objects arrive at their destination in the same way they are sent from the source. We all know deserialisation isn't always true to what you initially put in. Therefore you can register your own (de)serializer for complex objects.

from cereal_lazer import register_class
import pandas as pd

register_class(
    'DataFrame', # Register the object as this name
    pd.DataFrame, # Register the class
    lambda x: x.to_json(), # Register a serializer
    lambda x: pd.read_json(x) # Register a deserializer
)

These objects are registered in a global context using the cereal_lazer library. The client is then using the library to (de)serialize.

Using the client

It's important tot re-iterate on the fact that this is a self-building client. That means the way you interract with this client depends on external input. To have a practical example, consider the following SQLA models defined server-side:

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    birth_date = db.Column(db.Date)

class Computer(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)
    vendor = db.Column(db.Unicode)
    purchase_time = db.Column(db.DateTime)
    owner_id = db.Column(db.Integer, db.ForeignKey('person.id'))
    owner = db.relationship(
        'Person', backref=db.backref('computers', lazy='dynamic'))
    owner_name = association_proxy('owner', 'name')
    peers = association_proxy('owner', 'computers')

Based on these models, flask-restless-datamodel will generate the input for the client to build itself, allowing for an SQLA-like interface.

if we hook up our client to our server app, we'll be able to do all neat kinds of stuff

C is for Crayon

Now that we have some server side models exposed, the models will be available on the client side and we can jump right in and create some objects.

from restless_client import Client

c = Client(url='http://localhost:5000/api')
maurice = c.Person(name='Maurice')
roy = c.Person(name='Roy')

beast = c.Computer(name='TheBeast', vendor='Pear', owner=maurice)
server = c.Computer(name='Server', vendor='Pingu', owner=maurice)
pc = c.Computer(name='pc', vendor='Doors', owner=roy)

# Save objects on the server
c.save()

# Alternatively, you can save on a per-instance basis
beast.save()

Note that if we disregard the c.save() statement, and run beast.save() instead, that the maurice instance is a dependency of beast.owner and will be unsaved at the time we call beast.save(). The client should be able to resolve these unsaved dependencies and will save them first

R is for Rainbow

Loading objects can be done in serveral ways. The object models have a query attribute that is accessible to perform all read operations

Getting all instances from a given class

everyone = c.Person.query.all()

Getting an instance based on the id

maurice = c.Person.query.get(1)

Shorthand for all/get

Due to all and get being often used methods, they have been enabled with a shorthand on the object model itself

everyone = c.Person.all()
maurice = c.Person.get(1)

Filtering

maurice = c.Person.query.filter(c.Person.name == 'Maurice')
maurice = c.Person.query.filter_by(name='Maurice')
# limit the results to 3
some_people = c.Person.query.limit(3).all()
# offset results, ignoring the first 2
some_people = c.Person.query.offset(2).all()
# order by name
everyone = c.Person.query.order_by(name='asc').all()
# get the first instance
maurice = c.Person.query.first()
# get the last instance
maurice = c.Person.query.last()
# expect only one result
maurice = c.Person.query.one()
# expect only one result, or no result
maurice = c.Person.query.one_or_none()

# filtering over relations, get all people that own a computer with Pear vendor
maurice = c.Person.query.filter(c.Person.computers.has_(c.Computer.vendor == 'Pear'))

U is for you and me

Updating is just as easy as creating objects. The library is built in a way that it flags dirty attributes, and only sends the necessary data to the server.

cmptr = c.Computer.query.one()
cmptr.vendor = 'Robot'
cmptr.save()

D is for ... delete

cmptr = c.Computer.query.one()
cmptr.delete()

Note that executing delete is instant, and calling the save is not needed.

Running remote object methods

As promised, this library provides an RPC-like feature that allows you to run the methods defined on your SQLA models. It's nearly nowhere as advanced as other RPCs out there, but it at least provides a way to emulate the interaction on models as if you were working with them on a server context.

The sending and receiving of complex objects does require some setup, but once this is done, doing remote method calls should run smoothly. (Although there are plenty of scenarios where remote execution might fail).

Anyway here's wonderwall

On the server we would define a model with the following method

class Person(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.Unicode, unique=True)

    def speak(self, what_to_say):
        return "Errr... uh... ehm... {}?".format(what_to_say)

Which we can then remotely run by doing

maurice = c.Person.query.filter(c.Person.name == 'Maurice')
print(maurice.speak("I'd rather send an email"))

Future plans for running remote methods

Currently the client will crash if it tries to (de)serialize a complex object that is not yet registered. Going forward, it would be desired to apply a "no-crash" policy. The idea behind this is that the data is there, and it's not because (de)serialization failed, that the program should halt execution.

If a (de)serializer was not registered for a complex object, one will be emulated from the data available. Accessing data that is known to it will allow you to interact with it without issue, accessing functions or data that is unknown to the emulated object will result in an exception.

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

flask-restless-client-0.4.14.tar.gz (27.7 kB view hashes)

Uploaded Source

Built Distribution

flask_restless_client-0.4.14-py2.py3-none-any.whl (23.3 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page