This project puts the module together. Logged-in customers can review a product: a number of stars and a short text. Each review is a row in a new reviews table that keeps the product and the user who wrote it.
The author comes from the login. @login_required already finds the user from the token and passes it to your function. Save user["id"] as the author, never a user_id from the body: the client writes the body, so it could claim to be anyone. The same idea on another route:
@app.post("/wishlist")
@login_required
def add_to_wishlist(user):
data = request.get_json()
db.execute("INSERT INTO wishlist (user_id, product_id) VALUES (?, ?)", (user["id"], data["product_id"]))
db.commit()
return jsonify({"message": "Added"}), 201
Before saving, check the request in this order: the product must exist (404 if not), the stars must be a whole number from 1 to 5 (400, naming the field), and the user must not have reviewed this product before (409 Conflict). A request without a login never reaches your function: the decorator answers 401.
Ana signs up (she gets id 2) and logs in; the admin, already in the table, is user 1. Your route must answer:
POST /products/3/reviews no token {"stars": 5, "text": "Small but bright"}
→ 401 {"error": "Login required"}
POST /products/3/reviews Ana's token {"stars": 5, "text": "Small but bright", "user_id": 1}
→ 201 {"id": 1, "product_id": 3, "stars": 5, "text": "Small but bright", "user_id": 2}
POST /products/3/reviews Ana's token {"stars": 1, "text": "Changed my mind"}
→ 409 {"error": "You already reviewed this product"}
POST /products/1/reviews Ana's token {"stars": 6, "text": "Best mug ever"}
→ 400 {"error": "Stars must be a whole number from 1 to 5", "field": "stars"}
POST /products/1/reviews Ana's token {"stars": 0, "text": "Broke on day one"}
→ 400 {"error": "Stars must be a whole number from 1 to 5", "field": "stars"}
POST /products/99/reviews Ana's token {"stars": 4, "text": "Nice"}
→ 404 {"error": "Product not found"}
POST /products/3/reviews admin's token {"stars": 3, "text": "Too dim for reading"}
→ 201 {"id": 2, "product_id": 3, "stars": 3, "text": "Too dim for reading", "user_id": 1}
Your task: create the reviews table and add POST /products/<int:product_id>/reviews with @login_required. Check the product, the stars and an earlier review by the same user, save the review with the logged-in user as its author, and answer 201 with the new review.
Assignment
Follow the Coding Tutorial to create the reviews table and build the add_review route, one check at a time.
Hint
The function gets user first, from the decorator, then product_id from the path, like get_order. Do the checks in the order product, stars, earlier review, and only then insert and commit.
Free plan: 30 tutor messages. PRO is unlimited.
