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

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.

Related

Key error in flask web app - (KeyError: <weakref at 0x0000023357A05F80; to 'Flask' at 0x00000233559F45E0>)

I’m coding a web app using flask, I’ve connected to an external database service using the correct syntax (so the URI in the code is removed for obvious reasons additionally the secret key will not be "replace-later" in final product). I want to know if there is any way to pinpoint what is causing the key error? The libraries I’m using for the web app are flask, flask-wtf, flask-sqlalchemy; additionally as I want to connect to a Postgres’s db I have downloaded Postgres utilities from the Postgres website and I have a DB host service available too . What could be the solution to this error?
Here is the main app code, this contains app logic and whatnot:
from models import *
app = Flask(__name__)
app.secret_key = 'replace-later'
app.config['SQLALCHEMY_DATABASE_URI']='**REMOVED**'
db = SQLAlchemy(app)
#app.route("/", methods=['GET','POST'])
def index():
reg_form = RegistrationForm()
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
user_object = User.query.filter_by(username=username).first()
if user_object:
return "Username has been taken"
user = User(username=username, password=password)
db.session.add(user)
db.session.commit()
return "Added to DB"
Here is the link to the the db model which interacts with the database I have set up as well as the main app file in order to add user's usernames and passwords:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
""" User model """
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
here is an image of the traceback and the error: image
I have resolved this issue by removing the duplicate "db" variable from both files and moving the database table model (class User) to the main python file (first one shown).
This however presents a new problem now I am trying to add a custom validator to another file I have for form logic this however with the previous changes gives me basically the same error. Here is the code I've ended up on.
There are 3 files:
This is the main web-app logic file containing routes and etc:
from wtform_fields import *
from libs import Flask, render_template
app = Flask(__name__)
app.secret_key = 'replace-later'
app.config['SQLALCHEMY_DATABASE_URI']='**REMOVED**'
# database model
db = SQLAlchemy(app)
class User(db.Model):
""" User model """
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
password = db.Column(db.String(), nullable=False)
#app.route("/", methods=['GET','POST'])
def index():
reg_form = RegistrationForm()
if reg_form.validate_on_submit():
username = reg_form.username.data
password = reg_form.password.data
user = User(username=username, password=password)
db.session.add(user)
db.session.commit()
return "Added to DB"
return render_template("index.html", form=reg_form)
if __name__ == "__main__":
app.run(debug=True)
This is the libs file which holds all the imported libraries - This was created as i thought this might be causing the error
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import InputRequired, Length, EqualTo, ValidationError
This is the form logic file:
from libs import SQLAlchemy, FlaskForm, StringField, PasswordField, SubmitField, InputRequired, Length, EqualTo, ValidationError
from application import User
class RegistrationForm(FlaskForm):
'''Registration Form'''
username = StringField('username_label',
validators=[InputRequired(message="Username Required"),
Length(min=4, max=25, message="Username must be between 4-20 charecters.")]
)
password = PasswordField('password_label',
validators=[InputRequired(message="Password Required"),
Length(min=8, max=25, message="Password must be between 8-25 charecters.")]
)
conf_pswd = PasswordField('conf_pswd_label',
validators=[InputRequired(message="Retype Password"),
EqualTo('password', message="Passwords must match")]
)
submit_button = SubmitField('create')
def validate_username(self, username):
user_object = User.query.filter_by(username=username.data).first()
if user_object:
raise ValidationError("Username already exists")
This is the traceback for the error in the edited files: Traceback
My question is how can I firstly roll back the code back to the first instance (I'm using git so I am sure this is in some way possible) then resolve the key error in the first instance so that the DB model is in a separate file also that there is no libs file; Additionally if how would I be able to adapt the code in the second instance to match the first (This is optional as I have a general idea on how to do it but help would be appreciated). Also are there any suggestions as to how I could tidy up my code so it's more understandable and formatted.
The main question is how do I fix the KeyError (In the first instance)?
I've tried just about everything - checking for memory leaks checking for duplicate keys and variables and stuff like that. When the issue is resolved I want the program to be able to add users and their passwords to the Database as well as allowing the fields to be checked using a custom validator - and other validators I may add.

Fix cicular import when importing Flask blueprint

I'm trying to import a Flask blueprint into my main file. However, when I do from flask_first.login_user.login import login, I get cannot import name 'login' from partially initialized module 'flask_first.login_user.login' (most likely due to a circular import) (C:\Users\Max\PycharmProjects\python1\flask_first\login_user\login.py). How do I fix this?
flask_first/login_user/login.py:
from flask_first.main import *
from flask import Blueprint
from flask_first.main import users, db
from flask_sqlalchemy import SQLAlchemy
from datetime import timedelta
#login.route('/')
def login():
error = None
if request.method == 'POST': # checking if the method is POST, it means we got a query from button
if request.form['nm'] == 'admin' or request.form['ps'] == 'secret':
flash("You were successfully logged in into the admin's user page")
session.permanent = True
session['user'] = request.form['nm']
password = request.form['ps']
password = session['password']
return redirect(url_for('administrating'))
else: # if we are not admins, continue with this code
flash(f"You were successfully logged in", category='success')
session.permanent = True # setting the bool of session to permanent
session['user'] = request.form[
'nm'] # we are just saying that the session['user']= the name, which we typed into the field
user1 = session['user'] # user1 not a function
session['password'] = request.form['ps'] # session['password']= field, in which we typed our password
password1 = request.form['ps']
found_user = users.query.filter_by(name=user1,
password=password1).first() # we are filtering all the users in the database by the name and password, we typed while logging in
if found_user: # if we have found this user, we say that the email he typed previously is now in the field of email
session[
'email'] = found_user.email # we are saying that the email user typed previously, is now the session['email']
else:
usr = users(user1, '',
password1) # if we haven't found that user by name and password, we create a new one
db.session.add(usr)
db.session.commit()
return redirect(
url_for('user')) # redirecting to the user's page after logging in(using user's name)
else: # below is a standard script, which checks whether we are logged or not
if 'user' in session: # if user is already logged, it will download the user page.
flash('You are already logged in, to log out, type logout')
return redirect(url_for('user'))
else:
flash("You have not logged yet", category='success')
return render_template('login_user.html', error=error) # if it didn't go properly, we force the comeback
# to the login_user page again
flask_first/main.py:
from datetime import timedelta
from flask import Flask, redirect, url_for, render_template, request, session, flash
from flask_sqlalchemy import SQLAlchemy
from flask_first.admin.second import second
from flask_first.login_user.login import login
app = Flask(__name__)
app.secret_key = 'hello world'
app.register_blueprint(second, url_prefix='/test')
app.register_blueprint(login, url_prefix='login_user')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3.html' # access to the SQL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.permanent_session_lifetime = timedelta(minutes=5) # setting the time for long-lasting session
db = SQLAlchemy(app)
As the error suggests, you have a circular dependency issue. You are importing login from main and main from login. Modules cannot do that.
Check out the "problems" section In this wiki https://en.m.wikipedia.org/wiki/Circular_dependency

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.

Passing POST call values using url_for in Flask

I am new to Flask and have been working on an existing app for its login module. It has to be delivered asap. The login credentials validates by Active Directory authentication, which is working as expected. I am referring this flask link which says
url_for('main', page=2): return the internal URL /?page=2. All optional keyword arguments are treated as GET parameters.
My index page is loaded from here (working perfectly):
#app.route("/", methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
userName = request.form['username']
pwd = request.form['password']
try:
ldapobject = auth.bind(userName,pwd)
return redirect(url_for('scheduler')) ----> NEED TO PASS USER AND PWD TO scheduler function using post here
except ldap.LDAPError, error_message:
error = 'Invalid Credentials. Please try again'
return render_template('login.html', error=error)
My scheduler function:
#app.route("/home", methods=['POST'])
def scheduler():
# some code here
# WANT TO HAVE USER CREDENTIALS IN THIS FUNCTION FROM login page
return render_template("scheduler.html", scheduler=form)
Problem: How to pass user's credentials to scheduler method as a POST call
If you want to persist user data between your application pages - you will need to store some session data on user side, e.g. cookies.
flask-login module (https://flask-login.readthedocs.org/en/) can do all the dirty work for you.
In short you need to create User class with several required fields and methods (https://flask-login.readthedocs.org/en/latest/#your-user-class) and specify several methods with decorators to load and check users that are being authenticated:
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app=app)
#login_manager.user_loader
def load_user(username):
return User(username)
#login_manager.request_loader
def load_from_request(request):
return User.auth_is_valid(request.authorization)
After that you can mark any routes that need user credentials with #login_requied decorator and access logged in user via current_user variable:
from flask_login import login_required, current_user
#app.route("/user_needed")
#login_required
def some_function():
print(current_user)

Internal server error 500 after launching flask python

I am following the tutorial: https://pythonhosted.org/Flask-SQLAlchemy/quickstart.html but something I am missing. I am using Pycharm 4.0.6 as interpreter. Almost everything is working but when I add db.session.commit() it saying me: Internal Server Error 500
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
def __repr__(self):
return '<User %r>' % self.username
#app.route('/')
def hello_world():
db.create_all()
admin = User('admin', 'admin#example.com')
guest = User('guest', 'guest#example.com')
db.session.add(admin)
db.session.add(guest)
db.session.commit()
return 'Hello World'
if __name__ == '__main__':
app.run()
You've set the username and email fields to be unique. The first time you visit /, two users are created. The second time you visit, the view attempts to create and insert the same two users again. However, the usernames and emails already exist in the database, so it fails.
Creating an instance of a model with the same values is not the same as selecting it from the database. Instead, you should try to select the existing instances first, and only create new ones if the query did not return anything.
admin = User.query.filter(
User.username == 'admin' | User.email == 'admin#example.com'
).first()
if admin is None:
admin = User('admin', 'admin#example.com')
db.session.add(admin)
db.session.commit()
For a more in depth look, see the Unique recipe in the SQLAlchemy wiki.
It's better to set "app.debug = True" to get an error message that can help you troubleshoot, I believe #davidism already beat me to the answer, but just to add, you should catch SQlAlchemy errors as follows:
from sqlalchemy.exc import SQLAlchemyError
try:
db.session.commit()
except SQLAlchemyError as e:
reason=str(e)
flash(reason)
This way your application will continue to run and the error message will be flashed on the screen, making it easier for you and the end user to correct the mistake.
In my case, I was using an index.html template outside the templates folder.
When I moved the index.html file under templates folder the issue got resolved.

Categories