Flask error: RuntimeError: Working outside of application context

Question:

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)
Asked By: pc510895

||

Answers:

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.

Answered By: Detlef
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.