flask.session is losing data from one view to another - python

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')```

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.

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.

Storing and retrieving messages from a database

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.

Restricting Access to a webpage using python flask stripe payment

I have a basic flask app, where I charge customers to view a page
from flask import Flask, render_template, request, redirect, url_for
import stripe
app = Flask(__name__)
pub_key = 'pk_test_999999999'
secret_key = 'sk_test_999999'
stripe.api_key = secret_key
#app.route('/')
def index():
return render_template('index.html', pub_key=pub_key)
#app.route('/thank_you')
def thanks():
return render_template('thanks.html')
#app.route('/pay', methods=['POST'])
def pay():
customer = stripe.Customer.create(
email=request.form['stripeEmail'],
source=request.form['stripeToken']
)
charge = stripe.Charge.create(
customer=customer.id,
amount=19900,
currency='usd',
description='The Product'
)
return redirect(url_for('thanks'))
if __name__ == '__main__':
app.run(debug=True)
What I am trying to do is restrict access to the thank you page I want no one to access the thank_you by typing the whole url in the browser only paying customers get to see the thank you page even if someone type the whole url www.example.com/thank_you it will redirect to you haven't paid please pay
I thought about adding a login page and having a decorator only login customers, I did not like the idea I don't like to create such a barrier I want no customer info to deal with just pay and access page
Any ideas on how to do that?
Try something like this. Remember this is not completely secure. As I don’t know how your ids and tokens are generated. But it’s just for the sake of simplicity.
If you want something more secure check flask sessions or flask login packages.
customers_payed = []
#app.route('/pay', methods=['POST'])
def pay():
customer = stripe.Customer.create(
email=request.form['stripeEmail'],
source=request.form['stripeToken']
)
charge = stripe.Charge.create(
customer=customer.id,
amount=19900,
currency='usd',
description='The Product'
)
# add customer id to list maybe hash it with its email and token you can make this as hard to guess as you want
customers_payed.append(str(customer.id) + request.form['stripeToken'])
return redirect(url_for('thanks', customer_id=customer.id, token= request.form['stripeToken']))
#app.route('/thank_you')
def thanks():
customer_id = requests.args.get(“customer_id”)
token = requests.args.get(“token”)
# check if its in the list, maybe pop it if customer is only allowed once
if (customer_id+token) in customers_payed:
return render_template('thanks.html')
else:
return redirect(url_for(“replace with error page”))

Unit testing Flask function (with logged in user)

I'm new to Python and Flask but I'm gradually getting to grips with it. I've got so far into building an app and I'm now thinking I should start some unit testing. I really can't get my head around it though. I've read various docs, posts and examples but I can't seem to transfer this to my own code. I'm hoping if someone can show me how to write a test for one of my functions then things will fall into place. It's very tempting at this stage to ignore it and press on building my app.
#app.route('/user/<nickname>/create_bike', methods = ['GET', 'POST'] )
#login_required
def create_bike(nickname):
user = User.query.filter_by(nickname = nickname).first()
bikes = user.bikes.all()
bikecount = user.bikes.count()
form = CreateBike()
if form.validate_on_submit():
if bikecount < user.bike_allowance:
# let user create a bike
newbike = Bikes(bikename = form.bikename.data,
user_id = g.user.id, shared = SHARED )
db.session.add(newbike)
db.session.commit()
flash('Your new bike has been created')
return redirect(url_for('create_bike', nickname = nickname))
else:
flash("You have too many bikes")
return render_template('create_bike.html',
user = user,
form = form
)
UPDATE - Here's my working test
def test_create_bike(self):
u = User(nickname = 'john', email = 'john#example.com', account_type = "tester")
db.session.add(u)
db.session.commit()
# login user
with self.app as c:
with c.session_transaction() as sess:
sess['user_id'] = int(u.get_id())
# http://pythonhosted.org/Flask-Login/#fresh-logins
sess['_fresh'] = True
rv = c.post('/user/john/create_bike', data = dict(
bikename = 'your new bike',
user_id = sess['user_id'],
shared = 1
), follow_redirects = True)
assert 'Your new bike has been created' in rv.data
Your test is likely failing because that view requires a logged in user, and since you arent passing in any session data, you are being redirected to the login page (the data argument to .post is form data, available in request.form in your view). Before your assertion you can see what the response is to help you along the way:
print rv.status_code
print rv.location #None if not a redirect
There's some documentation around sessions in tests here, and if you're using Flask-Login like it looks you are, this answer shows you how to set the session up so you get a logged in user
You're on the right track.
Checking if the html page contains some piece of data that should be there is one the most common scenarios in testing web apps. You just need to repeat for all the other pages that you create. You can also add tests to see that creating a user follows all the validation rules that you've setup or that the username field is unique etc.

Categories