Skip to content

Commit

Permalink
Fix flask example and add schema_example
Browse files Browse the repository at this point in the history
Demonstrates an implementation of use_schema
  • Loading branch information
sloria committed Sep 27, 2015
1 parent bcd7cd5 commit 5a5d27e
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
2 changes: 1 addition & 1 deletion examples/flask_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def dateadd(value, addend, unit):
return jsonify({'result': result.isoformat()})

# Return validation errors as JSON
@app.errorhandler(400)
@app.errorhandler(422)
def handle_validation_error(err):
exc = err.data['exc']
return jsonify({'errors': exc.messages}), 400
Expand Down
119 changes: 119 additions & 0 deletions examples/schema_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Example implementation of using a marshmallow Schema for both request input
and output with a `use_schema` decorator.
Run the app:
$ python examples/schema_example.py
Try the following with httpie (a cURL-like utility, http://httpie.org):
$ pip install httpie
$ http GET :5001/users/
$ http GET :5001/users/42
$ http POST :5001/users/ usename=brian first_name=Brian last_name=May
$ http PATCH :5001/users/42 username=freddie
"""
import functools
from flask import Flask, request, jsonify
import random

from marshmallow import Schema, fields, post_dump
from webargs.flaskparser import parser

app = Flask(__name__)

##### Fake database and models #####

class Model:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)

def update(self, **kwargs):
self.__dict__.update(kwargs)

@classmethod
def insert(cls, db, **kwargs):
collection = db[cls.collection]
new_id = None
if 'id' in kwargs: # for setting up fixtures
new_id = kwargs.pop('id')
else: # find a new id
found_id = False
while not found_id:
new_id = random.randint(1, 9999)
if new_id not in collection:
found_id = True
new_record = cls(id=new_id, **kwargs)
collection[new_id] = new_record
return new_record

class User(Model):
collection = 'users'

db = {'users': {}}


##### use_schema #####

def use_schema(schema, list_view=False, locations=None):
"""Decorator for using a marshmallow schema for
(1) parsing a request's input and
(2) serializing its output to a JSON response.
"""
def decorator(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
use_args_wrapper = parser.use_args(schema, locations=locations)
# Function wrapped with use_args
func_with_args = use_args_wrapper(func)
ret = func_with_args(*args, **kwargs)
# Serialize and jsonify the return value
return jsonify(schema.dump(ret, many=list_view).data)
return wrapped
return decorator

##### Schemas #####

class UserSchema(Schema):
id = fields.Int(dump_only=True)
username = fields.Str()
first_name = fields.Str()
last_name = fields.Str()

class Meta:
strict = True

@post_dump(pass_many=True)
def wrap_with_envelope(self, data, many):
return {'data': data}


##### Routes #####

@app.route('/users/', methods=['GET', 'POST'])
@use_schema(UserSchema(), list_view=True)
def user_list(reqargs):
users = db['users'].values()
if request.method == 'POST':
User.insert(db=db, **reqargs)
return users


@app.route('/users/<int:user_id>', methods=['GET', 'PATCH'])
@use_schema(UserSchema())
def user_detail(reqargs, user_id):
user = db['users'].get(user_id)
if not user:
return jsonify({'message': 'User not found'}), 404
if request.method == 'PATCH' and reqargs:
user.update(**reqargs)
return user

# Return validation errors as JSON
@app.errorhandler(422)
def handle_validation_error(err):
exc = err.data['exc']
return jsonify({'errors': exc.messages}), 422

if __name__ == "__main__":
User.insert(db=db, id=42, username='fred', first_name='Freddie', last_name='Mercury')
app.run(port=5001, debug=True)

0 comments on commit 5a5d27e

Please sign in to comment.