Created
October 19, 2018 13:58
-
-
Save ilovetogetspamed/abdddd669f10c145da7ea869195e07e9 to your computer and use it in GitHub Desktop.
Flask Classful with Marshmallow Testing
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
import datetime | |
from flask import Flask, jsonify, request | |
from flask_sqlalchemy import SQLAlchemy | |
from sqlalchemy.exc import IntegrityError | |
from marshmallow import Schema, fields, ValidationError, pre_load | |
from flask_classful import FlaskView | |
app = Flask(__name__) | |
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/quotes.db' | |
db = SQLAlchemy(app) | |
##### MODELS ##### | |
class Author(db.Model): | |
id = db.Column(db.Integer, primary_key=True) | |
first = db.Column(db.String(80)) | |
last = db.Column(db.String(80)) | |
class Quote(db.Model): | |
id = db.Column(db.Integer, primary_key=True) | |
content = db.Column(db.String, nullable=False) | |
author_id = db.Column(db.Integer, db.ForeignKey('author.id')) | |
author = db.relationship( | |
'Author', | |
backref=db.backref('quotes', lazy='dynamic'), | |
) | |
posted_at = db.Column(db.DateTime) | |
##### SCHEMAS ##### | |
class AuthorSchema(Schema): | |
id = fields.Int(dump_only=True) | |
first = fields.Str() | |
last = fields.Str() | |
formatted_name = fields.Method('format_name', dump_only=True) | |
def format_name(self, author): | |
return '{}, {}'.format(author.last, author.first) | |
# Custom validator | |
def must_not_be_blank(data): | |
if not data: | |
raise ValidationError('Data not provided.') | |
class QuoteSchema(Schema): | |
id = fields.Int(dump_only=True) | |
author = fields.Nested(AuthorSchema, validate=must_not_be_blank) | |
content = fields.Str(required=True, validate=must_not_be_blank) | |
posted_at = fields.DateTime(dump_only=True) | |
# Allow client to pass author's full name in request body | |
# e.g. {"author': 'Tim Peters"} rather than {"first": "Tim", "last": "Peters"} | |
@pre_load | |
def process_author(self, data): | |
author_name = data.get('author') | |
if author_name: | |
first, last = author_name.split(' ') | |
author_dict = dict(first=first, last=last) | |
else: | |
author_dict = {} | |
data['author'] = author_dict | |
return data | |
author_schema = AuthorSchema() | |
authors_schema = AuthorSchema(many=True) | |
quote_schema = QuoteSchema() | |
quotes_schema = QuoteSchema(many=True, only=('id', 'content')) | |
class QuotesView(FlaskView): | |
def index(self): | |
quotes = Quote.query.all() | |
result = quotes_schema.dump(quotes, many=True) | |
return jsonify({'quotes': result}) | |
def get(self, pk): | |
try: | |
quote = Quote.query.get(pk) | |
except IntegrityError: | |
return jsonify({'message': 'Quote could not be found.'}), 400 | |
result = quote_schema.dump(quote) | |
return jsonify({'quote': result}) | |
def post(self): | |
json_data = request.get_json() | |
if not json_data: | |
return jsonify({'message': 'No input data provided'}), 400 | |
# Validate and deserialize input | |
try: | |
data = quote_schema.load(json_data) | |
except ValidationError as err: | |
return jsonify(err.messages), 422 | |
first, last = data['author']['first'], data['author']['last'] | |
author = Author.query.filter_by(first=first, last=last).first() | |
if author is None: | |
# Create a new author | |
author = Author(first=first, last=last) | |
db.session.add(author) | |
# Create new quote | |
quote = Quote( | |
content=data['content'], | |
author=author, | |
posted_at=datetime.datetime.utcnow(), | |
) | |
db.session.add(quote) | |
db.session.commit() | |
result = quote_schema.dump(Quote.query.get(quote.id)) | |
return jsonify({ | |
'message': 'Created new quote.', | |
'quote': result, | |
}) | |
QuotesView.register(app) | |
##### API ##### | |
''' | |
@app.route('/authors') | |
def get_authors(): | |
authors = Author.query.all() | |
# Serialize the queryset | |
result = authors_schema.dump(authors) | |
return jsonify({'authors': result}) | |
@app.route('/authors/<int:pk>') | |
def get_author(pk): | |
try: | |
author = Author.query.get(pk) | |
except IntegrityError: | |
return jsonify({'message': 'Author could not be found.'}), 400 | |
author_result = author_schema.dump(author) | |
quotes_result = quotes_schema.dump(author.quotes.all()) | |
return jsonify({'author': author_result, 'quotes': quotes_result}) | |
@app.route('/quotes/', methods=['GET']) | |
def get_quotes(): | |
quotes = Quote.query.all() | |
result = quotes_schema.dump(quotes, many=True) | |
return jsonify({'quotes': result}) | |
@app.route('/quotes/<int:pk>') | |
def get_quote(pk): | |
try: | |
quote = Quote.query.get(pk) | |
except IntegrityError: | |
return jsonify({'message': 'Quote could not be found.'}), 400 | |
result = quote_schema.dump(quote) | |
return jsonify({'quote': result}) | |
@app.route('/quotes/', methods=['POST']) | |
def new_quote(): | |
json_data = request.get_json() | |
if not json_data: | |
return jsonify({'message': 'No input data provided'}), 400 | |
# Validate and deserialize input | |
try: | |
data = quote_schema.load(json_data) | |
except ValidationError as err: | |
return jsonify(err.messages), 422 | |
first, last = data['author']['first'], data['author']['last'] | |
author = Author.query.filter_by(first=first, last=last).first() | |
if author is None: | |
# Create a new author | |
author = Author(first=first, last=last) | |
db.session.add(author) | |
# Create new quote | |
quote = Quote( | |
content=data['content'], | |
author=author, | |
posted_at=datetime.datetime.utcnow(), | |
) | |
db.session.add(quote) | |
db.session.commit() | |
result = quote_schema.dump(Quote.query.get(quote.id)) | |
return jsonify({ | |
'message': 'Created new quote.', | |
'quote': result, | |
}) | |
''' | |
if __name__ == '__main__': | |
db.create_all() | |
app.run(debug=True, port=5000) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment