文件结构:
example
| models.py
| test_flaskr.py
|--flaskr
| | __init__.py
现在就上代码
# models.py
# 版权所有
import os
from sqlalchemy import Column, String, Integer, create_engine
from flask_sqlalchemy import SQLAlchemy
import json
database_name = "bookshelf_test"
database_path = "postgres://username@localhost:5432/bookshelf_test"
db = SQLAlchemy()
"""
setup_db(app)
binds a flask application and a SQLAlchemy service
"""
def setup_db(app, database_path=database_path):
app.config["SQLALCHEMY_DATABASE_URI"] = database_path
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.app = app
db.init_app(app)
db.create_all()
"""
Book
"""
class Book(db.Model):
__tablename__ = "bookstest"
id = Column(Integer, primary_key=True)
title = Column(String)
author = Column(String)
rating = Column(Integer)
def __init__(self, id, title, author, rating):
self.id = id
self.title = title
self.author = author
self.rating = rating
def insert(self):
db.session.add(self)
db.session.commit()
def update(self):
db.session.commit()
def delete(self):
db.session.delete(self)
db.session.commit()
def format(self):
return {
"id": self.id,
"title": self.title,
"author": self.author,
"rating": self.rating,
}
# __init__.py
# 版权所有
import os
from flask import Flask, request, abort, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
import random
from models import setup_db, Book
BOOKS_PER_SHELF = 8
def paginate_books(request, selection):
page = request.args.get("page", 1, type=int)
start = (page - 1) * BOOKS_PER_SHELF
end = start + BOOKS_PER_SHELF
books = [book.format() for book in selection]
current_books = books[start:end]
return current_books
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__)
setup_db(app)
CORS(app)
# CORS Headers
@app.after_request
def after_request(response):
response.headers.add(
"Access-Control-Allow-Headers", "Content-Type,Authorization,true"
)
response.headers.add(
"Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS"
)
return response
@app.route("/books")
def retrieve_books():
selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
if len(current_books) == 0:
abort(404)
return jsonify(
{
"success": True,
"books": current_books,
"total_books": len(Book.query.all()),
}
)
@app.route("/books/<int:book_id>", methods=["PATCH"])
def update_book(book_id):
body = request.get_json()
try:
book = Book.query.filter(Book.id == book_id).one_or_none()
if book is None:
abort(404)
if "rating" in body:
book.rating = int(body.get("rating"))
book.update()
return jsonify(
{
"success": True,
}
)
except:
abort(400)
@app.route("/books/<int:book_id>", methods=["DELETE"])
def delete_book(book_id):
try:
book = Book.query.filter(Book.id == book_id).one_or_none()
if book is None:
abort(404)
book.delete()
selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
return jsonify(
{
"success": True,
"deleted": book_id,
"books": current_books,
"total_books": len(Book.query.all()),
}
)
except:
abort(422)
@app.route("/books", methods=["POST"])
def create_book():
body = request.get_json()
new_id = body.get("id", None)
new_title = body.get("title", None)
new_author = body.get("author", None)
new_rating = body.get("rating", None)
try:
book = Book(id = new_id, title=new_title, author=new_author, rating=new_rating)
book.insert()
selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
return jsonify(
{
"success": True,
"created": book.id,
"books": current_books,
"total_books": len(Book.query.all()),
}
)
except:
abort(422)
@app.errorhandler(404)
def not_found(error):
return (
jsonify({"success": False, "error": 404, "message": "resource not found"}),
404,
)
@app.errorhandler(422)
def unprocessable(error):
return (
jsonify({"success": False, "error": 422, "message": "unprocessable"}),
422,
)
@app.errorhandler(400)
def bad_request(error):
return jsonify({"success": False, "error": 400, "message": "bad request"}), 400
@app.errorhandler(405)
def not_found(error):
return (
jsonify({"success": False, "error": 405, "message": "method not allowed"}),
405,
)
return app
# test_flaskr.py
# 版权所有
import os
import unittest
import json
from flaskr import create_app
from models import setup_db, Book
class BookTestCase(unittest.TestCase):
"""This class represents the trivia test case"""
def setUp(self):
"""Define test variables and initialize app."""
self.app = create_app()
self.client = self.app.test_client
self.database_name = "bookshelf_test"
self.database_path = "postgres://username@localhost:5432/bookshelf_test"
setup_db(self.app, self.database_path)
self.new_book = {"id": 21, "title": "Example_title", "author": "Example_author", "rating": 5}
def tearDown(self):
"""Executed after reach test"""
pass
def test_get_paginated_books(self):
res = self.client().get("/books")
data = json.loads(res.data)
self.assertEqual(res.status_code, 200)
self.assertEqual(data["success"], True)
self.assertTrue(data["total_books"])
self.assertTrue(len(data["books"]))
def test_404_sent_requesting_beyond_valid_page(self):
res = self.client().get("/books?page=1000", json={"rating": 1})
data = json.loads(res.data)
self.assertEqual(res.status_code, 404)
self.assertEqual(data["success"], False)
self.assertEqual(data["message"], "resource not found")
def test_update_book_rating(self):
res = self.client().patch("/books/5", json={"rating": 1})
data = json.loads(res.data)
book = Book.query.filter(Book.id == 5).one_or_none()
self.assertEqual(res.status_code, 200)
self.assertEqual(data["success"], True)
self.assertEqual(book.format()["rating"], 1)
def test_400_for_failed_update(self):
res = self.client().patch("/books/5")
data = json.loads(res.data)
self.assertEqual(res.status_code, 400)
self.assertEqual(data["success"], False)
self.assertEqual(data["message"], "bad request")
def test_create_new_book(self):
res = self.client().post("/books", json=self.new_book)
data = json.loads(res.data)
self.assertEqual(res.status_code, 200)
self.assertEqual(data["success"], True)
self.assertTrue(data["created"])
self.assertTrue(len(data["books"]))
def test_405_if_book_creation_not_allowed(self):
res = self.client().post("/books/45", json=self.new_book)
data = json.loads(res.data)
self.assertEqual(res.status_code, 405)
self.assertEqual(data["success"], False)
self.assertEqual(data["message"], "method not allowed")
# Delete a different book in each attempt
def test_delete_book(self):
res = self.client().delete("/books/6")
data = json.loads(res.data)
book = Book.query.filter(Book.id == 6).one_or_none()
self.assertEqual(res.status_code, 200)
self.assertEqual(data["success"], True)
self.assertEqual(data["deleted"], 6)
self.assertTrue(data["total_books"])
self.assertTrue(len(data["books"]))
self.assertEqual(book, None)
def test_422_if_book_does_not_exist(self):
res = self.client().delete("/books/1000")
data = json.loads(res.data)
self.assertEqual(res.status_code, 422)
self.assertEqual(data["success"], False)
self.assertEqual(data["message"], "unprocessable")
# Make the tests conveniently executable
if __name__ == "__main__":
unittest.main()
要想测试这API,得在本地创建数据库来测试,查看如何创建数据库
接着在Terminal里, 运行后端
$ cd example
$ export FLASK_APP=flaskr
$ export FLASK_ENV=development
$ flask run
打开另一个Terminal
$ cd example
$ python3 test_flaskr.py
这样就能看到测试有没有成功。