问题
I'm trying to create a SelectField using a mongodb query, but so far I haven't been successful:
# forms.py in blueprint
CATEGORIES = []
for item in db.Terms.find():
CATEGORIES.append((item['slug'], item['name']))
class TermForm(Form):
category = SelectField(
choices=CATEGORIES,
validators=[Optional()])
But I get an exception:
Traceback (most recent call last):
File "/home/one/Projects/proj/manage.py", line 14, in <module>
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
File "/home/one/Projects/proj/app/__init__.py", line 27, in create_app
from app.term.models import Term, TermCategory
File "/home/one/Projects/proj/app/term/__init__.py", line 5, in <module>
from . import views
File "/home/one/Projects/proj/app/term/views.py", line 7, in <module>
from .forms import TermForm, CategoryForm
File "/home/one/Projects/proj/app/term/forms.py", line 48, in <module>
for item in db.Terms.find():
File "/home/one/.venv/proj/lib/python3.4/site-packages/flask_mongokit.py", line 238, in __getattr__
self.connect()
File "/home/one/.venv/proj/lib/python3.4/site-packages/flask_mongokit.py", line 196, in connect
host=ctx.app.config.get('MONGODB_HOST'),
AttributeError: 'NoneType' object has no attribute 'app'
If anyone could shed a little more light upon the subject, I would be very appreciative.
回答1:
See the bottom of this answer for what you really want, this first section is just to explain the immediate error you're getting.
You are running code that depends on an application context outside of such a context. You'll need to run the code that populates CATEGORIES
inside an application context so that the Flask-MongoKit db can get a connection.
It looks like you're using an application factory, so refactor your code a bit so that you can populate the collection while creating the app (you need access to the app to set up a context).
Put the code inside a function, then import and call that function in the factory within a context.
# forms.py
CATEGORIES = []
def init():
for item in db.Terms.find():
CATEGORIES.append((item['slug'], item['name']))
# add to create_app function (even the import)
def create_app(conf):
#...
from app.term import forms
with app.app_context():
forms.init()
#...
If you you're using blueprints, you can add a function that executes when the blueprint is registered, so the app factory doesn't need to know about all the details. Move all imports such as views
to inside this registration function. The changes from above are not needed.
# at the bottom of app/term/__init__.py
# assuming blueprint is called bp
@bp.record_once
def register(state):
with state.app.app_context():
from . import forms, views
However, neither of these are probably what you actually want in this case. It looks like you're trying to dynamically set the choices for a form field to the current values in the database. If you do this as you are now, it will be populated once when the app starts and then never change even if the database entries change. What you really want to do is set the choices in the form's __init__
method.
class TestForm(Form):
category = SelectField()
def __init__(self, *args, **kwargs):
self.category.kwargs['choices'] = [(item['slug'], item['name']) for item in db.Terms.find()]
Form.__init__(self, *args, **kwargs)
回答2:
It looks like you're calling a method that needs an app context (db.Terms.find
) without having the context available. You can populate the choices in the view instead:
# forms.py
class TermForm(Form):
category = SelectField(validators=[Optional()])
# views.py
form = TermForm()
form.category.choices = [(item['slug'], item['name']) for item in db.Terms.find()]
来源:https://stackoverflow.com/questions/28133859/how-to-populate-wtform-select-field-using-mongokit-pymongo