'Flask: How to access current user when creating forms?

I have found this question asked in various ways slightly different but the answers seemed old or not quite what I was looking for. I have a functioning Flask application with Flask-Security and some WTF Forms. Users can login, fill up forms and all, everything is fine.

Now, I have the need to access a user setting (shirt size) while creating a form so it is displayed to the user as they fill the form (so this is not a validation problem). I added to the form file: from flask_security import current_user to access the setting current_user.ts_size but I am faced with the AttributeError: 'NoneType' object has no attribute 'ts_size'

I understand that this means that current_user is of NoneType, the question is why? I am using the same import line in the file that defines the views without any problem. The views are decorated with auth_required so by the time a form needs to be created and displayed to the user, it is already check that the user is authenticated. What am I missing?

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
from flask_security import current_user

class JacketForm(FlaskForm):
  material = StringField(validators=[DataRequired()],)
  size = StringField(validators=[DataRequired()], description=current_user.ts_size) 


Solution 1:[1]

There are a few things going on on your code. First - remember that the field definitions are class variables - so those don't change when a form is instantiated. Second - StringField doesn't have a keyword argument 'cur_size' - it does have a keyword argument 'default' - which can be a callable!

So try something like:

size = StringField(validators=[DataRequired()], default=lambda: current_user.ts_size)

Solution 2:[2]

The statement:

size = StringField(validators=[DataRequired()], description=current_user.ts_size

may be correct, but not appropriate.

Remember Flask handles view functions, models, templates and forms separately. Therefore, the size field should only be allowed to pick a field which is Sting type, as you have specified and perhaps validators. Not the user as you have specified above, if the concept of separation is to be maintained.

In the view function is where you now link the size field to the current user. You can do this by: While in the view function that calls the the JacketForm, call the current user id using current_user.get_id. With the user's id then use it query for the ts_size field, pick the string value and store it there. You will need the import statement from flask_login import current_user in the view function to ensure that it is the current user, that is, the user logged in, is the one allowed to fill in the JacketForm. Remember also this will happen if your model - database correctly connects the user table to the jacket table or whichever name you have given it, through correct relationship.

I have just tried to explain what I will do without writing any code, mainly because you have only provided one form. It is not clear how you have defined your view functions or tables in the database to have an overall picture. And if you are able to it, then you will be learning. The take way is that the size field in the JacketForm should never have the user specified there, only the type of field and validators. What the field finally picks is provided in the view function that makes use of the JacketForm.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 jwag
Solution 2