-
-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix flask example and add schema_example
Demonstrates an implementation of use_schema
- Loading branch information
Showing
2 changed files
with
120 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |