user3476725
user3476725

Reputation:

current_app and importing config settings in Flask

Recently, I've started building an app with Flask. It's pretty easy to use, yet powerful and fun to use. Unfortunately, I'm having problems understanding how the app context works. I know about current_app, app_context and that current_app references are supposed to be used inside requests, but that's probably part of my problem.

I'm building an app with the following folder structure:

app/
     main/
         __init__.py
         error.py
         forms.py
         routes.py
     static/
     templates/
     __init__.py
     email.py
     models.py
config.py
run.py

I'm doing an from flask import current_app in routes.py to import config settings and it works as expected.

But when I import current_app in forms.py I can do whatever I want, but I always get this error:

Traceback (most recent call last):
  File "./run.py", line 6, in <module>
    app = create_app('development')
  File "/home/letmecode/Projects/python/flask/folder/app/__init__.py", line 34, in create_app
    from main import main as main_blueprint
  File "/home/letmecode/Projects/python/flask/folder/app/main/__init__.py", line 5, in <module>
    from . import routes, errors, forms
  File "/home/letmecode/Projects/python/flask/folder/app/main/routes.py", line 8, in <module>
    from .forms import (ChangePasswordForm, ChangeEmailForm, CreateItemForm, RetrievePasswordForm, LoginForm,
  File "/home/letmecode/Projects/python/flask/folder/app/main/forms.py", line 132, in <module>
    class CreateItemForm(Form):
  File "/home/letmecode/Projects/python/flask/folder/app/main/forms.py", line 133, in CreateItemForm
    print current_app
  File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/werkzeug/local.py", line 362, in <lambda>
    __str__ = lambda x: str(x._get_current_object())
  File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/werkzeug/local.py", line 302, in _get_current_object
    return self.__local()
  File "/home/letmecode/.virtualenvs/folder/local/lib/python2.7/site-packages/flask/globals.py", line 34, in _find_app
    raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context

forms.py is just a module that contains flask-wtf form classes and validator functions.

My problem is, that I want to reference the app context inside there, in order to get settings that are used throughout the app.

What am I doing wrong here? I guess what happens inside forms.py is not part of a request and therefore fails somehow. But how else am I supposed to reference the app context, then?

Edit:

#forms.py

from flask import current_app

#[...]

class CreateItemForm(Form):
    title = TextField('Title', [
        validators.Length(min=3, max=140),
        validators.Regexp('^[a-zA-Z][a-zA-Z0-9 ]+$', 0, 'Item titles must start with a letter and consist of numbers and letters, only')
    ])
    description = TextAreaField('Description', [
        validators.Length(min=3, max=1000),
    ])
    tags = TextField('Tags')
    branch = SelectField(
        'Branch',
        choices=current_app.config['BRANCHES']
    )

Upvotes: 3

Views: 7041

Answers (2)

Sean Vieira
Sean Vieira

Reputation: 160025

The underlying issue is that CreateItemForm and all of the attributes are created when the forms module is imported for the first time. That means that the branch field is created whenever import .form is run, and therefore current_app is accessed then:

# app/main/routes.py
from .forms import ( ..., CreateItemForm, ...)

Looking at your stack trace, this happens in your create_app call - most likely, your create_app looks something like this:

def create_app(config_name):
    app = Flask('some-name-here')
    app.config.from_SOMETHING(config_name)

    # Register things here
    from main import main as main_blueprint

    app.register_blueprint(...)

    return app

At # Register things here you can simply create an app_context and import everything inside of that:

# Register things here
# Creating an explicit application context to allow
# easy access to current_app.config, etc.
with app.app_context():
    from main import main as main_blueprint
    app.register_blueprint(...)

return app

Upvotes: 6

user3476725
user3476725

Reputation:

I found that I was being an idiot. You don't have to provide choices per form model. You can (and should) provide them via the view/route function e.g.:

form.select_field.choices = [('value1', 'label1'), ('value2', 'label2')]

This doesn't explicitly answer my question, but renders it somewhat obsolete. However, I won't accept my own answer as a solution (yet), because I am curious as to whether it's at all possible to import configuration options from via app/current_app in this case, without getting hacky. If my curiosity won't be satisfied, I'll accept my own answer.

I wish the wtforms/flask-wtf documentation would focus less on the obvious and more on the not so obvious.

Upvotes: 1

Related Questions