0

I'm trying to post the following JSON and save to a MySQL database on a Flask server and Python 2.7, restless framework and SQLAlchemy with curl:

curl -i -H "Accept: application/json" -X POST  -d '{"attribute_id": "1", "product_id": "44","text":"Something","language":"1"}' http://seroney-pc:5000/api/attributes

{
    "attribute_id": "1",
    "product_id": "44",
    "text": "Something",
    "language": "1"
}

My code is as follows:

from flask import Flask,request,jsonify, abort
from flask_sqlalchemy import SQLAlchemy
import flask_restless


app = Flask(__name__)
db = SQLAlchemy(app)
manager = flask_restless.APIManager(app)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:seroney@localhost:3306/test'


class Attributes(db.Model):
    __tablename__ = 'oc_product_attribute'
    product_id = db.Column(db.Integer,primary_key=True)
    attribute_id = db.Column(db.Integer,primary_key=True)
    language_id = db.Column(db.Integer,primary_key=True)
    text=db.Column(db.String)

@app.route('/api/attributes/',methods=['GET'])
def getProductAttributes():
    if request.method =='GET':
        results = Attributes.query.limit(10).offset(0).all()
        json_results = []
        for result in results:
            d = {
                'product_id':result.product_id,
                'attribute_id':result.attribute_id,
                'language_id':result.language_id,
                'text':result.text
            }
            json_results.append(d)

        return jsonify(items = json_results)

@app.route('/api/attributes/', methods=['POST'])

def postProductAttributes():
    product_id = request.json['product_id']
    attribute_id = request.json['attribute_id']
    language_id = request.json['language_id']
    text = request.json['text']
    if product_id is None or attribute_id is None or language_id is None or text is None:
        return jsonify({"message": "Error."}), 400
    new_attrib = (product_id,attribute_id,language_id,text)
    db.session.add(new_attrib)
    db.session.commit()
    return jsonify({'message' :'Attribute Created successfully'}), 200

if __name__ == '__main__':
    app.run(debug=True)

When I POST I keep I getting an Internal Server Error. Any help is highly appreciated.

The traceback is:

seroney-pc - - [23/Dec/2014 20:48:40] "POST /api/attributes HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1453, in dispatch_request
    self.raise_routing_exception(req)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1439, in raise_routing_exception
    raise FormDataRoutingRedirect(request)

Note: this exception is only raised in debug mode

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Seroney
  • 805
  • 8
  • 26
  • 1
    A 500 Internal Server Error is *logged*. What does your console say? There should be a traceback, please include that in your post. – Martijn Pieters Dec 22 '14 at 19:28
  • How are you posting the JSON data? Are you using a Content-Type header of `application/json` on it? – Martijn Pieters Dec 22 '14 at 19:29
  • @MartijnPieters, I've update the question with the traceback info and curl post command. Thank you. Any Help is highly appreciated – Seroney Dec 23 '14 at 18:02
  • Is that the whole traceback? The exception message may hold more information still, and is indented to the same level as `Traceback` (e.g. no indentation at all). – Martijn Pieters Dec 23 '14 at 18:27
  • The exception documentation string is: *This exception is raised by Flask in debug mode if it detects a redirect caused by the routing system when the request method is not GET, HEAD or OPTIONS. Reasoning: form data will be dropped.* – Martijn Pieters Dec 23 '14 at 18:28

1 Answers1

4

You are posting to the URL without a / at the end, but you specified your route with a trailing /. When you do that, Flask issues a redirect to the 'canonical' URL, with the /.

Because you are using POST, the post data will be lost, so in debug mode an exception is raised instead to inform you that you should use the trailing slash in your post instead.

Had you looked at the body of the error message, you'd have seen something like:

FormDataRoutingRedirect: A request was sent to this URL (http://seroney-pc:5000/api/attributes) but a redirect was issued automatically by the routing system to "http://seroney-pc:5000/api/attributes/". The URL was defined with a trailing slash so Flask will automatically redirect to the URL with the trailing slash if it was accessed without one. Make sure to directly send your POST-request to this URL since we can't make browsers or HTTP clients redirect with form data reliably or without user interaction.

Note: this exception is only raised in debug mode

See the Rule Format documentation:

URL rules that end with a slash are branch URLs, others are leaves. If you have strict_slashes enabled (which is the default), all branch URLs that are visited without a trailing slash will trigger a redirect to the same URL with that slash appended.

Note that your curl POST uses the wrong header; you need to set the Content-Type header. Your view is looking for the language_id key, but your post contains only a language key, you need to correct that too:

curl -i -H "Content-Type: application/json" -X POST \
     -d '{"attribute_id": "1", "product_id": "44","text":"Something","language_id":"1"}' http://seroney-pc:5000/api/attributes/

The Accept header may be useful too, but it is used for negotiating the response content type, and you have your views hardcoded to return JSON.

Your code creating the database object is also incorrect, you need to call the model and pass in the arguments as separate arguments, then pass in the resulting to session.add():

new_attrib = Attributes(*new_attrib)
db.session.add(new_attrib)

but just reusing the JSON object would be easier here:

db.session.add(Attributes(**request.json))
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I have appended the trailing / but I'm getting a new traceback Traceback (most recent call last): raise ValueError('View function did not return a response') ValueError: View function did not return a response – Seroney Dec 23 '14 at 18:35
  • @Seroney: Are you sure you are posting to the right route? When I test your code with the corrected POST I get a different error (because you don't set the correct Content-Type header). – Martijn Pieters Dec 23 '14 at 18:47
  • @Seroney: with extra fixes I can get your code to work just fine. – Martijn Pieters Dec 23 '14 at 18:55
  • I have one quick question how do i validate the request.json e.g if Attributes.query.filter_by(**request.json).first is not None:in essence what I'm trying to achieve is a product should not have multiple attribute_id I'm using sqlalchemy and i'm novice at it. :-) – Seroney Dec 24 '14 at 16:05
  • validation should be a product should not be able to create same attribute, need to validate against db – Seroney Dec 24 '14 at 16:12
  • @Seroney: You could just catch the exception SQLAlchemy would throw if not all keys are present or are of the wrong type. If there are integrity constraints (like the primary key being unique) then trying to commit will throw an exception too. – Martijn Pieters Dec 24 '14 at 20:45
  • This was so simple, so frustrating, so grateful! Thank you! – ExoWanderer Jul 09 '20 at 11:25