文件结构
example
| models.py
| test_flaskr.py
|--flaskr
| | __init__.py
是在这篇基础上,添加代码,主要是在__init__.py 和 test_flaskr.py
直接上代码:
# __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)
# 增加了这一行
search = body.get('search', None)
try:
# 增加了这个 if 判断语句
if search:
selection = Book.query.order_by(Book.id).filter(Book.title.ilike('%{}%'.format(search)))
current_books = paginate_books(request, selection)
return jsonify(
{
'success': True,
'books': current_books,
'total_books': len(selection.all())
}
)
else:
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": "Anansi Boys", "author": "Neil Gaiman", "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_get_book_search_with_results(self):
# 'Still' 得是在数据库里
res = self.client().post('/books', json={'search': 'Still'})
data = json.loads(res.data)
self.assertEqual(res.status_code, 200)
self.assertEqual(data['success'], True)
# 注意这里,是用 assertTrue()
self.assertTrue(data['total_books'])
# 这里的 1, 得跟数据库数据一致
self.assertEqual(len(data['books']), 1)
# 添加这个函数
def test_get_book_search_without_results(self):
res = self.client().post('/books', json={'search': 'applejacks'})
data = json.loads(res.data)
self.assertEqual(res.status_code, 200)
self.assertEqual(data['success'], True)
self.assertEqual(data['total_books'], 0)
self.assertEqual(len(data['books']), 0)
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):
# 这里的 9,得跟数据库一致,如果数据库没有,就得换个数字
res = self.client().delete("/books/9")
data = json.loads(res.data)
# 这里的 9 也是一样道理
book = Book.query.filter(Book.id == 9).one_or_none()
self.assertEqual(res.status_code, 200)
self.assertEqual(data["success"], True)
# 这里的9也是一样道理
self.assertEqual(data["deleted"], 9)
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()
要运行这个测试程序,步骤:
$ cd example
$ export FLASK_APP=flaskr
$ export FLASK_ENV=development
$ flask run
$ cd example
$ python3 test_flaskr.py
结果如下:
..........
----------------------------------------------------------------------
Ran 10 tests in 0.498s
OK