more.body-model

load_json infrastructure for Morepath


Keywords
morepath, validation
License
BSD-3-Clause
Install
pip install more.body-model==0.1

Documentation

more.body_model: load_json infrastructure for Morepath

The idea is to recognize on an application-level what kind of JSON content is being posted, and convert it into a Python object. You can use this application object with request.body_obj. With body_model you can then write views that specifically match that kind model.

To use it you have to subclass your application from more.body_model.BodyModelApp:

from more.body_model import BodyModelApp

class App(BodyModelApp):
    pass

load_json

The App.load_json directive lets you define a function that turns incoming JSON into a Python object. This behavior is shared by all views in the application. We detect JSON with the type field Item and interpret it as an Item instance, and pass through everything else:

@App.load_json()
def load_json(json, request):
    if json.get('type') != 'Item':
        return json
    return Item(json['x'])

When you write a json view you automatically get the Item instance as the body_obj attribute of the request:

@App.json(model=Collection, request_method='POST')
def collection_post(self, request):
    collection.add(request.body_obj)
    return "success!"

You can write views that match on the class of body_obj by specifying body_model:

@App.json(model=Collection, request_method='POST', body_model=Item)
def collection_post_item(self, request):
    collection.add(request.body_obj)
    return "success!"

body_model

To define JSON body conversion code generally for an application we can use App.load_json:

@App.load_json()
def load_json(json, request):
   if is_valid_document_json(json):
      return Document(title=json['title'],
                      author=json['author'],
                      content=json['content'])
   # fallback, just return plain JSON
   return json

Now we get a Document instance in Request.body_obj, so we can simplify document_collection_post:

@App.json(model=DocumentCollection, request_method='POST')
def document_collection_post(self, request):
    if not isinstance(request.body_obj, Document):
       raise webob.exc.HTTPUnprocessableEntity()
    result = self.add(request.body_obj)
    return request.view(result)

To only match if body_obj is an instance of Document we can use body_model on the view instead:

@App.json(model=DocumentCollection, request_method='POST', body_model=Document)
def document_collection_post(self, request):
    result = self.add(request.body_obj)
    return request.view(result)

Now you get the 422 error for free if no matching body_model can be found. You can also create additional POST views for DocumentCollection that handle other types of JSON content this way.