dpgaspar / Flask-AppBuilder

Simple and rapid application development framework, built on top of Flask. includes detailed security, auto CRUD generation for your models, google charts and much more. Demo (login with guest/welcome) - http://flaskappbuilder.pythonanywhere.com/
BSD 3-Clause "New" or "Revised" License
4.72k stars 1.37k forks source link

base_filters: g.user – AttributeError: 'user' #2114

Closed schrecksim closed 1 year ago

schrecksim commented 1 year ago

Environment

Flask-Appbuilder version: 4.3.6

pip freeze output:

Describe the expected results

The g.user should return the username to filter on it. If I place a string like UserGlobalProvider.username == 'someuser' it works totally fine. The g.user should return the username, if possible, as a string to use it like shown below.

I know that probably I could do the solution shorter like: def get_user_ben(): return g.user.ben_id

but it needs to work like that as well for getting user information in other context of the application as well.

# my sec_models file
class UserGlobalProvider(User):

        __tablename__ = 'ab_user'

            ben_id = Column(VARCHAR(19))

            def __repr__(self):
                return self.username

# view utils file
def get_user_ben():
    user_ben_id = app_builder.session.query(UserGlobalProvider.ben_id).filter(UserGlobalProvider.username == g.user)
    return user_ben_id

# view file
base_filters = [['ben_id', FilterEqual, get_user_ben()]]

Describe the actual results

Failed to create app.
Traceback (most recent call last):
  File "/home/simon/workspace/meta-map-ui/meta_map_ui/app.py", line 28, in create_app
    init_views()
  File "/home/simon/workspace/meta-map-ui/meta_map_ui/views/tech/data_domain.py", line 9, in <module>
    from ..data.models import DataModelsView
  File "/home/simon/workspace/meta-map-ui/meta_map_ui/views/data/models.py", line 24, in <module>
    class DataModelsBaseView(BaseClassViewQuality):
  File "/home/simon/workspace/meta-map-ui/meta_map_ui/views/data/models.py", line 62, in DataModelsBaseView
    base_filters = [['ben_id', FilterEqual, get_user_ben()]]
  File "/home/simon/workspace/meta-map-ui/meta_map_ui/views/view_utils.py", line 27, in get_user_ben
    user_ben_id = app_builder.session.query(UserGlobalProvider.ben_id).filter(UserGlobalProvider.username == g.user)
  File "/home/simon/workspace/meta-map-ui/.venv/lib/python3.10/site-packages/flask/ctx.py", line 54, in __getattr__
    raise AttributeError(name) from None
AttributeError: user

It seems that the g.user isn't set, although besides what is written above there is nothing else done to the security manager than shown above with a sec_view to implement the new attribute ben_id. The sec.py file is also done like written in the documentation.

finnkr539 commented 1 year ago

Hey,

I can't test the code right now, but I think that the problem is with the way the base filter is written. What you want is this:

from flask_appbuilder.models.sqla.filters import FilterEqualFunction
base_filters = [['ben_id', FilterEqualFunction, get_user_ben]]

The two changes are using FilterEqualFunction instead of Filter Equal (obviously) and only giving the function without calling it right away (might be easier to overlook :)). See also here in the documentation: https://flask-appbuilder.readthedocs.io/en/latest/advanced.html#base-filtering And i recommend scanning this page to get an idea of what filters are available to you: https://github.com/dpgaspar/Flask-AppBuilder/blob/master/flask_appbuilder/models/sqla/filters.py#L17

To add some context: FAB tries to evaluate your filter when starting the app, at which point there is no g.user available. That's why the error points at Flask not being able to start the app instead of the error occuring when trying to access the view. The new code let's FAB evaluate the filter when accessing the view, so now you have g.user and everything should work out nicely.

Let me know if it's still not working!

schrecksim commented 1 year ago

Works now, thanks for your support. I wasn't aware about the difference between FilterEqual & FilterEqualFunction. Thanks for explaining.