Forums

Writing POST method values to MySQL

Hi Everyone, my use case is this: i want to receive a webhook from Tradingview (JSON formatted) and store it in the database. (Later I'll act on trading, but I will want to record actions to a database so this is my first step. I followed the tutorial that utilizes Flask with MySQL and had no problems writing the comments to the table and displaying them. To that code, I added the POST method. It seems to be handled, but nothing is getting stored.

My send_post.py

import requests
response = requests.post(
    "http://pegasus163.pythonanywhere.com/food",
    json=dict(
        title="A title",
        description="A description"
    )
)
assert response.status_code == 200, "success"

I run it in the window and I get this response:

Traceback (most recent call last):
  File "/home/pegasus163/mysite/send_post.py", line 9, in <module>
    assert response.status_code == 200, "success"
AssertionError: success
>>>

The Access log shows:

3.93.173.252 - - [02/Jul/2022:10:16:41 +0000] "POST /food HTTP/1.1" 500 1184 "-" "python-requests/2.25.1" "3.93.173.252" response-time=0.012

The error logs show:

2022-07-02 10:16:41,003: Error running WSGI application
2022-07-02 10:16:41,009: sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.str' is not mapped
2022-07-02 10:16:41,009:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2069, in __call__
2022-07-02 10:16:41,009:     return self.wsgi_app(environ, start_response)
2022-07-02 10:16:41,009: 
2022-07-02 10:16:41,009:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2054, in wsgi_app
2022-07-02 10:16:41,009:     response = self.handle_exception(e)
2022-07-02 10:16:41,009: 
2022-07-02 10:16:41,009:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2051, in wsgi_app
2022-07-02 10:16:41,009:     response = self.full_dispatch_request()
2022-07-02 10:16:41,010: 
2022-07-02 10:16:41,010:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1501, in full_dispatch_request
2022-07-02 10:16:41,010:     rv = self.handle_user_exception(e)
2022-07-02 10:16:41,010: 
2022-07-02 10:16:41,010:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1499, in full_dispatch_request
2022-07-02 10:16:41,010:     rv = self.dispatch_request()
2022-07-02 10:16:41,010: 
2022-07-02 10:16:41,010:   File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1485, in dispatch_request
2022-07-02 10:16:41,010:     return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
2022-07-02 10:16:41,010: 
2022-07-02 10:16:41,010:   File "/home/pegasus163/mysite/flask_app.py", line 39, in foo_page
2022-07-02 10:16:41,010:     db.session.add(message)
2022-07-02 10:16:41,011: 
2022-07-02 10:16:41,011:   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/orm/scoping.py", line 163, in do
2022-07-02 10:16:41,011:     return getattr(self.registry(), name)(*args, **kwargs)
2022-07-02 10:16:41,011: 
2022-07-02 10:16:41,011:   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/orm/session.py", line 2020, in add
2022-07-02 10:16:41,011:     replace_context=err,
2022-07-02 10:16:41,011: 
2022-07-02 10:16:41,011:   File "/usr/local/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 182, in raise_
2022-07-02 10:16:41,011:     raise exception

flask_app.py

import requests
from flask import Flask, redirect, render_template, request, url_for, Response
from flask_sqlalchemy import SQLAlchemy
import dbaccess as config

app = Flask(__name__)
app.config["DEBUG"] = True


SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}@{hostname}/{databasename}".format(
    username=config.username,
    password=config.password,
    hostname=config.hostname,
    databasename=config.databasename,
)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)

class Comment(db.Model):

    __tablename__ = "comments"

    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(4096))

@app.route("/", methods=["GET", "POST"])   # from tutorial, works fine
def index():
    if request.method == "GET":
        return render_template("main_page.html", comments=Comment.query.all())
        comment = Comment(content=request.form["contents"])
        db.session.add(comment)
        db.session.commit()

@app.route('/food', methods=["POST"]) # just write message to db to confirm
def foo_page():
    message = "test food"
    db.session.add(message)
    db.session.commit()
    return Response(status=200)

@app.route('/foo', methods=["POST"])  # handle the POST request, write description to db
def form_example():
    if request.method == 'POST':
        request_data = request.get_json()
        if request_data:
            if 'title' in request_data:
                title = request_data['title']
            if 'description' in request_data:
                description = request_data['description']
        db.session.add(description)
        db.session.commit()
    return redirect(url_for('index'))

@app.route('/webhook', methods=['POST'])
def webhook():
    if request.method == 'POST':
        comment = Comment(content=request.form["contents"])
        db.session.add(comment)
        db.session.commit()
        return Response(status=200)
    else:
        abort(400)

    return redirect(url_for('index'))

Note that the send_post is POSTing to /food. I just expect it to make an entry to the database.

Thanks for reading this far. Please advise if you can.

If you look at the difference between foo_page, the view that is raising the error, and the index and webhook views, you'll see that in foo_page you're trying to add a string directly to the database rather than an object of a class that is defined to have a mapping to a SQL table.

You can only add objects to the DB if they are subclasses of db.Model, as otherwise SQLAlchemy has no idea where to put them in the database. So, you could rewrite foo_page to insert a Comment object with the contents of the post:

@app.route('/food', methods=["POST"]) # just write message to db to confirm
def foo_page():
    message = "test food"
    db.session.add(Comment(content=message))
    db.session.commit()
    return Response(status=200)

...but it would probably be better to write a different class to hold this different kind of message.

Bless you. Worked.

Excellent, thanks for confirming!