Reputation:
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
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
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