2

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.

eneepo
  • 1,407
  • 3
  • 21
  • 32

2 Answers2

3

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()]
nathancahill
  • 10,452
  • 9
  • 51
  • 91
0

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)
davidism
  • 121,510
  • 29
  • 395
  • 339