Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Flask REST API Resourceful Blueprints Marshalling

Matthew McElwee
Matthew McElwee
16,545 Points

Trouble with POST (Flask Rest API)

I have followed the instructions in the video. Get requests are working great, however I can't get post data to send. I keep getting an error that says: "The browser (or proxy) sent a request that this server could not understand."

Any help would be great.

2 Answers

David Lin
David Lin
35,864 Points

Can you post your code?

Here's my courses.py and reviews.py, and the POST works fine:

courses.py :

from flask import jsonify, Blueprint, abort

from flask_restful import (Resource, Api, reqparse, url_for,
                           inputs, fields, marshal, marshal_with)

import models

course_fields = {
    'id': fields.Integer,
    'title': fields.String,
    'url': fields.String,
    'reviews': fields.List(fields.String)
}

def add_reviews(course):
    course.reviews = [url_for('resources.reviews.review', id=review.id)
                      for review in course.review_set]
    return course

def course_or_404(course_id):
    try:
        course = models.Course.get(models.Course.id==course_id)
    except models.Course.DoesNotExist:
        abort(404)
    else:
        return course

class CourseList(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument(
            'title',
            required=True,
            help='No course title provided',
            location=['form', 'json']
        )
        self.reqparse.add_argument(
            'url',
            required=True,
            help='No course URL provided',
            location=['form', 'json'],
            type=inputs.url
        )
        super().__init__()

    def get(self):
        courses = [marshal(add_reviews(course), course_fields)
                   for course in models.Course.select()]
        return {'courses': courses}

    @marshal_with(course_fields)
    def post(self):
        args = self.reqparse.parse_args()
        course = models.Course.create(**args)
        return (add_reviews(course), 201,
                {'Location': url_for('resources.courses.course', id=course.id)})

class Course(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument(
            'title',
            required=True,
            help='No course title provided',
            location=['form', 'json']
        )
        self.reqparse.add_argument(
            'url',
            required=True,
            help='No course URL provided',
            location=['form', 'json'],
            type=inputs.url
        )
        super().__init__()

    @marshal_with(course_fields)
    def get(self, id):
        return add_reviews(course_or_404(id))

    @marshal_with(course_fields)
    def put(self, id):
        args = self.reqparse.parse_args()
        course = course_or_404(id)
        query = course.update(**args)
        query.execute()
        course = course_or_404(id)
        return (course, 200,
                {'Location': url_for('resources.courses.course', id=id)})

    def delete(self, id):
        course = course_or_404(id)
        query = course.delete()
        query.execute()
        return '', 204, {'Location': url_for('resources.courses.courses')}

courses_api = Blueprint('resources.courses', __name__)
api = Api(courses_api)
api.add_resource(
    CourseList,
    '/api/v1/courses',
    endpoint='courses'
)
api.add_resource(
    Course,
    '/api/v1/courses/<int:id>',
    endpoint='course'
)

reviews.py :

from flask import jsonify, Blueprint, abort

from flask_restful import (Resource, Api, reqparse, inputs, url_for,
                           fields, marshal, marshal_with)

import models

review_fields = {
    'id': fields.Integer,
    'for_course': fields.String,
    'rating': fields.Integer,
    'comment': fields.String(default=''),
    'created_at': fields.DateTime
}

def add_course(review):
    review.for_course = url_for('resources.courses.course', id=review.course.id)
    return review

def review_or_404(review_id):
    try:
        review = models.Review.get(models.Review.id==review_id)
    except models.Review.DoesNotExist:
        abort(404)
    else:
        return review

class ReviewList(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument(
            'course',
            type=inputs.positive,
            required=True,
            help='No course provided',
            location=['form', 'json']
        )
        self.reqparse.add_argument(
            'rating',
            required=True,
            type=inputs.int_range(1,5),
            help='No rating provided',
            location=['form', 'json'],
        )
        self.reqparse.add_argument(
            'comment',
            required=False,
            nullable=True,
            location=['form', 'json'],
            default=''
        )
        super().__init__()

    def get(self):
        reviews = [marshal(add_course(review), review_fields) for review in models.Review.select()]
        return {'reviews': reviews}

    @marshal_with(review_fields)
    def post(self):
        args = self.reqparse.parse_args()
        review = models.Review.create(**args)
        return (add_course(review), 201,
                {'Location': url_for('resources.reviews.review', id=review.id)})

class Review(Resource):
    def __init__(self):
        self.reqparse = reqparse.RequestParser()
        self.reqparse.add_argument(
            'course',
            type=inputs.positive,
            required=True,
            help='No course provided',
            location=['form', 'json']
        )
        self.reqparse.add_argument(
            'rating',
            type=inputs.int_range(1, 5),
            required=True,
            help='No rating provided',
            location=['form', 'json']
        )
        self.reqparse.add_argument(
            'comment',
            required=False,
            nullable=True,
            location=['form', 'json'],
            default=''
        )
        super().__init__()

    @marshal_with(review_fields)
    def get(self, id):
        return add_course(review_or_404(id))

    @marshal_with(review_fields)
    def put(self, id):
        args = self.reqparse.parse_args()
        review = review_or_404(id)
        query = review.update(**args)
        query.execute()
        review = add_course(review_or_404(id))
        return (review, 200,
                {'Location': url_for('resources.reviews.review', id=id)})

    def delete(self, id):
        review = review_or_404(id)
        query = review.delete()
        query.execute()
        return '', 204, {'Location': url_for('resoures.reviews.reviews')}

reviews_api = Blueprint('resources.reviews', __name__)
api = Api(reviews_api)
api.add_resource(
    ReviewList,
    '/reviews',
    endpoint='reviews'
)
api.add_resource(
    Review,
    '/review/<int:id>',
    endpoint='review'
)

Hi the post does not work for me , can anyone help me , I used the code above, but I get { "message": { "title": "No course title provided" } }

Thanks