- you are starting a new large project; there are over 20 entitites!
- you have just a week, working with your JavaScript/UI developer, to deliver a mockup to your customer.
Have a rest. Im 90% cases, all your entities behave the same way. You already can transform your entities to JSON and back again (I already wrote about it). So why worry?
Okay, You are smart and You use Flask and Flask-RESTful
A universal real-only REST handler
First, we will create a simple readl-only REST controller template, so your UI guy can start.
from flask import abort, request
from flask.ext.restful import Resource, reqparse
from myapp.lib import database
from myapp import model as M
class ReadOnlyRestfulResource(Resource):
Entity = None
def get(self, id=None):
if not id:
query = database.session.query(self.Entity)
return [ entity.simple_object() for entity in query ]
entity = database.session.query(self.Entity).get(id)
if not entity:
abort(404)
return entity.simple_object()
Okay, so we can create a module "views" like this - we just name each restful resource class as it's corresponding model class:
from myapp.lib.restful import ReadOnlyRestfulResource
from myapp import model as M
__all__ = ['Person', 'LegalEntity', 'Contract']
class Person(ReadOnlyRestfulResource):
Entity = M.Person
class LegalEntity(ReadOnlyRestfulResource):
Entity = M.LegalEntity
class Contract(ReadOnlyRestfulResource):
Entity = M.Contract
Now, just import them in your app.py and attach routes:
from flask.ext.restful import Resource, Api
# create RESTfull API
api = Api(app)
# other app details skipped
import views
for view_name in views.__all__:
try:
view = getattr(views, view_name)
view_name = view_name.lower()
api.add_resource(view, '/' + view_name + '/', '/' + view_name, '/' + view_name + '/')
app.logger.debug(u"Registered view {}".format(view_name))
except Exception as e:
app.logger.warn(e)
Great! Now, accessing /person will provide your UI guy with a list of all persons, and /contract/13 with contract #13, etc. Fill your database with sample data and you almost have your mockup alpha ready.
Next you fill your database with 1000+ rows of sample Contract entities... and notice, that a list of all contracts is too large. You only want a contract id, datem and come status in the full list. So let's implement different filtering in 'get-all' and 'get-one'. Remember, our 'simple_object' method accepts a list of fields, so let's use it.
from flask import abort, request
from flask.ext.restful import Resource, reqparse
from myapp.lib import database
from myapp import model as M
class ReadOnlyRestfulResource(Resource):
Entity = None
fields_all = None
fields_one = None
def get(self, id=None):
if not id:
query = database.session.query(self.Entity)
return [ entity.simple_object(self.fields_all) for entity in query ]
entity = database.session.query(self.Entity).get(id)
if not entity:
abort(404)
return entity.simple_object(self.fields_one)
And next we add some field filtering, if we want to. We can skip any or both filters:
class Person(ReadOnlyRestfulResource):
Entity = M.Person
fields_all = ['id', 'name', 'email']
fields_one = ['id', 'name', 'email', 'telephone', 'legal_entity_id']
class LegalEntity(ReadOnlyRestfulResource):
Entity = M.LegalEntity
fields_all = ['id', 'display_name', 'vat_id']
class Contract(ReadOnlyRestfulResource):
Entity = M.Contract
fields_all = ['id', 'legal_entity_id', 'date_signed']
Okay, our output is much better. Next, we want some other filtering on the server-side. The reason is: we have 100+ customers, each has 100+ contracts; requesting all contracts... wow! I just want contracts for this customer!
class ReadOnlyRestfulResource(Resource):
Entity = None
fields_all = None
fields_one = None
def get(self, id=None):
if not id:
query = database.session.query(self.Entity)
for k,v in request.args.iteritems():
try:
column = self.Entity.__table__.c[k]
filter = column==v
query = query.filter(filter)
except:
pass
return [ entity.simple_object(self.fields_all) for entity in query ]
entity = database.session.query(self.Entity).get(id)
if not entity:
abort(404)
return entity.simple_object(self.fields_one)
Now we can request /contract?customer_id=13 - and we fetch only desired contracts.At this point, we can grab some Faxe, Spendrups or Lapin Kulta and have a rest, while our UI guy creates his templates, etc. Or we can even help him! :)
What's next?
- implement handling POST, e.g. creating new entities;
- add some advanced filtering;
- implement PATCH for updating existing entities.
No comments:
Post a Comment