Storing and retrieving messages from a database - python

I am trying to set up a webhook on PythonAnywhere to connect to Twilio. I am essentially trying to create a very simple chatbot with if statements which choose a response based on the text message that comes in. I am now trying to add a databsae into the mix so that the response can also be based on previous texts as well. I have managed to set up the database but cant seem to get the text messages that are received to be stored in the database.
I have tried going on various tutorials and answer websites like this one but have not managed to get any of the potential solutions to work. They either break the code entirely so that no messages are sent in response or still nothing is stored in the database.
Code currently used
from flask import Flask, request, redirect, url_for
from twilio.twiml.messaging_response import MessagingResponse
from flask_sqlalchemy import SQLAlchemy
from socket import gethostname
app = Flask(__name__)
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}#{hostname}/{databasename}".format(
username="UrbanMissions",
password="Hello123",
hostname="UrbanMissions.mysql.pythonanywhere-services.com",
databasename="UrbanMissions$ClueNumber",
)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
class Reply(db.Model):
__tablename__ = "messages"
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(4096))
[...]
if __name__ == '__main__':
db.create_all()
reply = Reply(content=request.values.get('Body', None)
db.session.add(reply)
db.session.commit()
if 'liveconsole' not in gethostname():
app.run()
#app.route('/', methods=['GET', 'POST', 'PUT'])
def main():
body = request.values.get('Body', None)
text = body.lower()
resp = MessagingResponse()
if text.find("start") == 0:
This is the code that seems to cause all the problems
("$reply = Reply(content=request.values.get('Body', None)
$db.session.add(reply)
$db.session.commit()")
it is my attempt to add the message that was sent in to the messages database but this doesn't seem to work. Any advice on how to get this to work to add the incoming message to the database would be really appreciated. Also what the code to retrieve the message again from the database would really help.

Related

Flask error: RuntimeError: Working outside of application context [duplicate]

This question already has answers here:
Flask-SQLAlchemy db.create_all() raises RuntimeError working outside of application context
(3 answers)
Closed 4 months ago.
This has been asked before, but none of the solutions work for me. When I try to connect a Flask application to an SQL database, I get this error.
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
the current application. To solve this, set up an application context
with app.app_context(). See the documentation for more information.
When I reference the Flask documentation, it says to input this code:
def create_app():
app = Flask(__name__)
app.config.from_object("project.config")
import project.models
with app.app_context():
db.create_all()
return app
However, I do not understand what import project.models should do, and when I try to do so, PyCharms notes that there is no module named 'project'.
Here is a copy of my code before attempting the solutions from the Flask documentation:
from flask import Flask, redirect, url_for, render_template, request, session, flash, current_app
from datetime import timedelta
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.secret_key = "hello"
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.permanent_session_lifetime = timedelta(days=1)
db = SQLAlchemy(app)
class users(db.Model):
_id = db.Column("id", db.Integer, primary_key=True)
name = db.Column(db.String(100))
email = db.Column(db.String(100))
def __init__(self, name, email):
self.name = name
self.email = email
#app.route("/")
def home():
return render_template('index.html')
#app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == "POST":
session.permanent = True
user = request.form["nm"]
session["user"] = user
flash("Login successful.", "info")
return redirect(url_for("user"))
else:
if "user" in session:
flash("Already logged in.")
return redirect((url_for("user")))
return render_template("login.html")
#app.route('/user', methods=["POST", "GET"])
def user():
email = None
if "user" in session:
user = session["user"]
if request.method == "POST":
email = request.form["email"]
session["email"] = email
flash("Email was saved!")
else:
if "email" in session:
email = session["email"]
return render_template("user.html", email=email)
else:
flash("Not logged in.")
return redirect(url_for("login"))
#app.route("/logout")
def logout():
flash("You have been logged out", "info")
session.pop("user", None)
session.pop("email", None)
return redirect((url_for("login")))
if __name__ == "__main__":
db.create_all()
app.run(debug=True)
Here you will find a detailed explanation of the application context.
Manually pushing an application context
Certain commands, such as the one that creates the database tables from the database models, require access to the context of the current instance of the application.
In order to provide this access, outside of functions that grant it by default, it is necessary to push the application context manually. This is done with a block that starts with with app.app_context():. An application context is available within this scope and is destroyed again after it is left.
In order to run your application without errors, you should make the call to create the tables in a block that has access to the application context.
Within a database model, the individual columns of the database table are defined, which are linked to the respective model at runtime of the application. However, the tables are not created until the db.create_all() command is executed. At the time this command is executed, the columns of the database models must be defined, the models included, and an application context created, which is also required for execution.
# Your model definitions or imports here.
with app.app_context():
db.create_all()
# ...
The following is your complete code including the block for creating the database tables. You are currently not using the tables within your code. However, on first run, a file named users.sqlite3 should be created. This should be found within the instance path under ./var/app-instance/.
from datetime import timedelta
from flask import (
Flask,
flash,
redirect,
render_template,
request,
session,
url_for
)
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.secret_key = 'hello'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.permanent_session_lifetime = timedelta(days=1)
db = SQLAlchemy(app)
# Definition of your database models including the columns for the tables,
# which are named without an explicit definition based on the name of the
# respective model.
class User(db.Model):
id = db.Column('id', db.Integer, primary_key=True)
name = db.Column(db.String(100))
email = db.Column(db.String(100))
def __init__(self, name, email):
self.name = name
self.email = email
# Creation of the database tables within the application context.
with app.app_context():
db.create_all()
#app.route('/')
def home():
return render_template('index.html')
#app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
session.permanent = True
user = request.form['nm']
session['user'] = user
flash('Login successful.', 'info')
return redirect(url_for('user'))
else:
if 'user' in session:
flash('Already logged in.')
return redirect(url_for('user'))
return render_template('login.html')
#app.route('/user', methods=['POST', 'GET'])
def user():
email = None
if 'user' in session:
user = session['user']
if request.method == 'POST':
email = request.form['email']
session['email'] = email
flash('Email was saved!')
else:
if 'email' in session:
email = session['email']
return render_template('user.html', email=email)
else:
flash('Not logged in.')
return redirect(url_for('login'))
#app.route('/logout')
def logout():
flash('You have been logged out', 'info')
session.pop('user', None)
session.pop('email', None)
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
The import statement from the documentation is only required if the definition of the database model is to be moved to another file.
To understand the import command used and the division of your code into multiple files, you should familiarize yourself with modules and packages. This allows you to break up your application into individual parts, which, for example, promote clarity as your application grows. There are, however, even more reasons to do this.
Flask Shell
If you use the above variant to initialize the database, the commands to create the tables are executed when the server starts. This is the easiest way to achieve the goal, no matter what system and file structure you use.
However, there is also the possibility to achieve the result manually. This way can also be used to manually store something in the database or query something, which will be very helpful in your development.
For this it is necessary to set the environment variable FLASK_APP and assign it the path to your application file and, if used, the factory function. Then, within the virtual environment, the flask shell can be started with the command of the same name.
As an alternative to setting an environment variable, it is also possible to start the shell with the parameter --app, which refers to the application file.
flask --app main shell
In contrast to the python command line interpreter, the shell has access to the application context and imports the application.
In order to create the database tables it is now necessary to import the variable from the application file to which SQLAlchemy is assigned. Usually this is db.
Now the tables can be created with db.create_all().
# Assuming your application file is in the same directory and
# named "main.py".
from main import db
db.create_all()
Flask Migrate
If you have a larger database with many tables or many changes to the tables, it is worth taking a look at the Flask-Migrate extension. Table definitions are created here in separate files and can then be executed using a command line interface. This variant is very popular with advanced users.

Problem with Flask Blueprints, canĀ“t remove the /home route, or app crashes

Hi there I'm creating a Flask web app and now I have to create more cruds, so I decided to modularize the app using Blueprints.
I have a Login function on main.py that allows me to enter the app interface:
app.route('/', methods=['GET', 'POST'])
def login():
# Output message if something goes wrong...
msg = ''
# Check if "username" and "password" POST requests exist (user submitted form)
if request.method == 'POST' and 'username' in request.form and 'password' in request.form:
# Create variables for easy access
username = request.form['username']
password = request.form['password']
# Check if account exists using MySQL
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute(
'SELECT * FROM accounts WHERE username = %s AND password = %s', (username, password,))
# Fetch one record and return result
account = cursor.fetchone()
# If account exists in accounts table in out database
if account:
# Create session data, we can access this data in other routes
session['loggedin'] = True
session['id'] = account['id']
session['username'] = account['username']
# Redirect to home page
return redirect(url_for('client.home'))
else:
# Account doesnt exist or username/password incorrect
msg = 'Incorrect username/password!'
# Show the login form with message (if any)
return render_template('index.html', msg=msg)
It redirects to this Blueprint:
from flask import Blueprint, render_template
from flask import render_template, request, redirect, url_for, session, flash
from flask_mysqldb import MySQL
import MySQLdb.cursors
import re
from extension import mysql
client = Blueprint('client', __name__,
static_folder="../static", template_folder="../templates")
#client.route('/')
def home():
if 'loggedin' in session:
cur = mysql.connection.cursor()
cur.execute('SELECT * FROM cliente')
data = cur.fetchall()
# User is loggedin show them the home page
return render_template('home.html', username=session['username'], cliente=data)
# User is not loggedin redirect to login page
return redirect(url_for('login'))
It works just fine, but with a condition. On my main.py I also have this:
#app.route('/home')
def home():
pass
And this is the problem, I don't know why I should keep this route on my main.py because if I delete it my app crashes and throws me this error:
werkzeug.routing.BuildError
werkzeug.routing.BuildError: Could not build url for endpoint 'home'. Did you mean 'client.home' instead?
I have no idea why does this happens.
Why should I keep this route? or What I'm doing wrong?
Could you please give me a hand?
I've trying to change the redirect to using multiple routes, but if I delete that /home route.. my app crashes anyway.
in url_for look for the name of the function ie url_for('function_name', parameters)
so to avoid the crash better to change the name of main.py home function to something else.
Solved: I had one ref to Home on other file: Layout.html.
Just removed the ref and it's solved

Thread that calls function in a HTTP request throws RuntimeError: Working outside of application context

I have route within my Flask application that process a POST request and then sends an email to the client using the Flask-Mail library. I have the route returning a response to the client from checking the database before sending out an email. The email is sent from an individual thread, but I am getting this error thrown when trying to send out the email: RuntimeError: Working outside of application context.. I know that it is being thrown because the request was destroyed before the thread could process its tasks. However, I am not sure how exactly I should fix this error, especially when the route gets processed by a Blueprint.
routes.py:
from flask import request, Blueprint, redirect, render_template
from flask_app import mail, db
from flask_app.users.forms import Form
from flask_app.models import User
from flask_mail import Message
from threading import Thread
import os, re
users = Blueprint("users", __name__)
#users.route("/newsletter-subscribe", methods=["GET", "POST"])
def newsletter_subscribe():
form = Form()
if form.validate_on_submit():
user = User(name=form.name.data, email=form.email.data)
db.session.add(user)
db.session.commit()
thread_a = Compute(user, request.host_url)
thread_a.start()
return "Success"
return "Failure"
class Compute(Thread):
def __init__(self, user, host_url):
Thread.__init__(self)
self.host_url = host_url
self.user = user
def run(self):
send_welcome_email(self.user, self.host_url)
print("Finished")
def send_email(user, host_url):
with mail.connect() as con:
html = render_template("email.html", name=user.name, host_url=host_url)
subject = ""
msg = Message(
subject=subject,
recipients=[user.email],
html=html
)
con.send(msg)
Seeing that this question is over a year old, you may have solved this already, but I'll post this here for in case anyone else comes across it later.
I think the issue you're having is the same I had, where the send function needs the current flask app context.
You can try this within your send_email function:
Instead of:
con.send(msg)
Replace it with:
with app.app_context():
con.send(msg)
Just also make sure that you include your app in your includes section.
Change this:
from flask_app import mail, db
To this:
from flask_app import app, mail, db
That should resolve the RuntimeError: Working outside of application context. issue.

How does Flask-login session works

Forgive me for my lack of knowledge. Am a complete newbie to flask and web technology concept.
I am in the process to build the login part of an app. After searching, I found flask login to be an option to use. After going through SQLAlchemy,Flask-login homepage, some blogs,tutorials,and going through questions on stack-oflow, tried to build a basic login part-code given below. I used SQLAlchemy, and database is POSTGres. This is just a start involving the login through email-password and session handling will involve more functions later.
In the code, I authenticate the user-id and password, and then assign corresponding UUID(primary key in User DB) from the database as a session variable, in order to create a session. Am I right in doing so?. In some 'stack-of' answers, it is mentioned that session-id is to be randomly generated after user authentication, and stored in a separate sessions table in database. Got confused.
I am passing UUID which is my primary key, as an 'id' for 'get_id' method. Is is right??
I tried implementing this code. However in chrome developement console, I see sessions, which dissappear after i logout.
import flask
from flask import Flask,render_template,request,url_for,redirect,session
from flask_sqlalchemy import SQLAlchemy
from flask_login import current_user, UserMixin, LoginManager, login_required, login_user,
logout_user
app = Flask(__name__)
db = SQLAlchemy(app)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:test#localhost/hw'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
app.config['SECRET_KEY'] = 'thisissecret'
class User(UserMixin,db.Model):
__tablename__ = 'user'
__table_args__ = {'schema': 'logging'}
user_id = db.Column(db.VARCHAR,primary_key = True)
first_name = db.Column(db.VARCHAR)
last_name = db.Column(db.VARCHAR)
email = db.Column(db.VARCHAR)
contact_no = db.Column(db.VARCHAR)
status = db.Column(db.BOOLEAN)
loginpassword = db.Column(db.VARCHAR)
def get_id(self):
return str(self.user_id)
#login_manager.user_loader
def load_user(id):
try:
return User.query.get(id)
except:
return None
#app.route('/logedin',methods=['POST'])
def logedin():
session.pop('id', None)
em = request.form['email']
pwd = request.form['password']
usr = User.query.filter_by(email = em).first()
if usr:
if usr and usr.loginpassword == pwd:
login_user(usr,remember = False)
session['id'] = usr.user_id
return ('you r logged in')
else:
return '<h1>user not found</h1>'
else:
#return render_template('testlogin.html')
return '<h1>user not found</h1>'
#app.before_request
def check_logedin():
if not 'id' in session:
return render_template('testlogin.html')
#app.route('/login')
def login():
if current_user is_authenticated():
return redirect(url_for('home'))
else:
return render_template('testlogin.html')
#app.route('/logout')
#login_required
def logout():
logout_user()
session.pop('id', None)
return 'you are logged out'
#app.route('/home')
#login_required
def home():
return ('The current user is in.')
if __name__ == '__main__':
app.run(debug=True)
Apologies if some silly things. But I am unable to make out this session thing. Appreciate your help. Thanks in advance
You say that you "assign corresponding [user id] as a session variable", but some say "that session-id is to be randomly generated after user authentication, and stored in a separate sessions table in database".
Those two things are not in conflict. A session ID is a value sent to the user. It identifies session data. What is important is that the session data is hidden from the user and that a valid session ID cannot be guessed by a user.
What you are doing with flask.session is fine. You are placing a variable into a session and flask is taking care of the rest (giving only a random session ID to the user).
All you need to do is save user id in the session:
session['id'] = usr.user_id
and later read user id from the session
user_id = session.get('id')
Note that the user_id read this way may be None, meaning the user is not logged in.
This does not keep session data in a database, at least by default, but that probably is not important in your case. A good reason to keep data in a data base would be for example if you have a distributed system in which several servers are serving the same website, so the user might log in in one server, but then access another server.

flask.session is losing data from one view to another

I'm coding a very simple flask web app so that the testers from the place where I'm working could test my deep learning model without download the project.
It has a view which receive a pdf file and a string in a forms (the input for the deep learning model) and another view which shows another string (the output for the deep learning model).
I'm trying to use the flask.session object to pick the data from the first view and pass it to the second view and after that I store it in the flask.g object so that I can display it in a Jinja template.
My problem is that the flask.session object is losing the data from one view to another. How to avoid that?
I'm not very worried with security issues because only the testers from my job will have access to the web app.
And sorry if the question is very stupid, I'm a complete newbie.
I know there is probably best ways to do that but I want to keep it as simple as possible because I'm only doing that to test the deep learning model easily.
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
bp = Blueprint('book', __name__, url_prefix='/book')
#bp.route('/initialpage', methods=('GET', 'POST'))
def initialpage():
if request.method == 'POST':
book_file = request.form['book']
parsed = parser.from_file(book_file)
book = parsed["content"]
question = request.form['question']
error = None
if not book:
error = 'Book is required.'
elif not question:
error = 'Question is required.'
if error is None:
session.clear()
session['book'] = book
session['question'] = question
print(session['question'])
return redirect(url_for('book.finalpage'))
flash(error)
return render_template('book/initialpage.html')
#bp.route('/finalpage')
def finalpage():
book_body = session.get('book')
book_question = session.get('question')
if book_body is None:
g.book = None
elif book_question is None:
g.question = None
else:
g.book = book_body
g.question = book_question
return render_template('book/finalpage.html')
The data stored in session['book'] and session['question'] is lost when passing from the '/initialpage' to the '/finalpage' .
I finally manage to solve this problem!
In my case I think the problem was that my data was too big to be passed by flask.session. I found that most browsers don't support a session cookie larger than ~4000 bytes and my data is like ~10 MB.
So my idea was to create a database to store the data and pass only an id (which allows to find the data in the database) by flask.session.
As database I used Python SQLite3.
The functions to connect to the database and to initialize it were coded in a separated file.
The code became the following:
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from flaskr.db import get_db
bp = Blueprint('book', __name__, url_prefix='/book')
#bp.route('/initialpage', methods=('GET', 'POST'))
def initialpage():
if request.method == 'POST':
book_file = request.form['book']
parsed = parser.from_file(book_file)
book = parsed["content"]
question = request.form['question']
db = get_db()
error = None
if not book:
error = 'Book is required.'
elif not question:
error = 'Question is required.'
if error is None:
if db.execute('SELECT book, question FROM bq').fetchone() is None:
db.execute('INSERT INTO bq (book, question) VALUES (?, ?)',(book, question))
db.commit()
bq = db.execute('SELECT * FROM bq WHERE (book, question) = (?, ?)',(book, question)).fetchone()
session.clear()
session['bq_id'] = bq['id']
print(bq['id'])
return redirect(url_for('book.finalpage'))
flash(error)
return render_template('book/initialpage.html')
#bp.route('/finalpage')
def finalpage():
bq_id = session.get('bq_id')
print(bq_id)
if bq_id is None:
g.bq = None
else:
g.bq = get_db().execute('SELECT * FROM bq WHERE id = ?', (bq_id,)).fetchone()
return render_template('book/finalpage.html')```

Categories