问题描述
我使用wtforms创建了一个注册表单。我在其中使用FormField,所以我不必再次重复一些形式的元素。但是每当我点击提交按钮,它总是让我错误的validate_on_submit方法调用。不明白为什么会发生这种情况。
我的 form.py
如下:
class ProfileInfoForm(Form):
firstname = TextField('firstname',validators =
[validators.Required(Please enter First name。)])
lastname = TextField('lastname',validators =
[validators.Required(Please enter last name。))
email = EmailField('email',validators =
[验证人。必填项(请输入您的有效电子邮件地址)])
性别= RadioField('gender',验证人=
[validators.Required(请选择性别)],
选项= [('女','女'),('男','男')])
dob = TextField('dob',验证者=
[validators.Required(Please选择出生日期。)])
languages = SelectMultipleField('languages',choices = [('','')],
validators =
[validators.Required(请选择'
atleast one \
language。)])
$ b $ class RegistrationForm(Form ):
profilefield = FormField(ProfileInfoForm)
password = PasswordField('password',
validators =
[validators.Required(请输入密码),
validators.Length(min = 8),
validators.EqualTo('confirm_password',
message ='密码并确认'
密码必须匹配')])
confirm_password = PasswordField('confirm_password',
validators =
[validators.Required(请输入
确认密码。)])
tnc = BooleanField('tnc',validators =
[validators.Required(请选择条款和\\ \\
Conditions)],default = False)
$ b $ submit = SubmitField('Create My Account')
注册
方法如下:
@ module.route('/ signup',methods = ['GET','POST'])
@handle_error
def signup():$ b $如果hasattr(g, 'user')和g.user:
#TODO:做一些操作,如果需要的话保持空白
返回重定向(url_for('index'))
else:
signup_form = RegistrationForm()
#为用户添加选项
signup_form.profilefield.languages.choices = getLanguages()
如果signup_f orm.validate_on_submit():
firstname = signup_form.profilefield.firstname.data
lastname = signup_form.profilefield.lastname.data
email = signup_form.profilefield.email.data
password = signup_form.password.data
#confirm_password = signup_form.confirm_password.data
gender = signup_form.profilefield.gender.data
dob = signup_form.profilefield.dob.data
languages = signup_form.profilefield.languages.data
tnc = signup_form.tnc.data
payload = {'firstname':firstname,'lastname':lastname,
'email':email ,'password':密码,'gender':gender,
'dob':dob,'languages':languages,
'tnc':('1'if tnc else'0')}
试试:
buildApiUrl = Buil dApiUrl()
response = requests.post(buildApiUrl.getUrl(user,signup),
data = payload)
if response.status_code == requests。 code.ok:
data = json.loads(response.text)
如果数据和数据中的状态为''status']!= 200:
flash(data ['message' ],category =error)
else:
flash(data ['message'] +
':您的帐户已成功创建! '
'请登录到您的帐户!',
category =success)
return request(exception)(url_for('index'))
requests.exceptions.RequestException:
flash('Internal Server side error occured',category =error)
return redirect(url_for('server_error',e ='500'))
return render_template ('public / index.html',
signup_form = signup_form,login_form = LoginForm())
HTML表单存在于
仅供参考:我将所有必填字段与实际需要的数据放在一起。当我调用validate_on_submit()时,仍然是错误的。我的代码有什么问题?
编辑:getLanguages是一种从数据库检索语言并放入选择列表的方法。这个功能正在按预期发生,我可以得到语言列表。
编辑2:在这里实现一件事。这是由于FormField发生的,因为我通过在RegistrationForm()方法中添加了ProfileInfoForm()的所有字段进行测试,并且一切正常,我可以注册。所以一些问题与FormField或我使用它的方式,但不知道哪里出问题。
发现这个问题不是用FormField,而是用我的ProfileInfoForm()。它总是返回false。还没有理由,但我想我可能不得不为这个问题写我自己的验证。任何想法?
编辑:
在转储我有以下(在这里使用pprint):
$ b $ pre $ {code $ {'SECRET_KEY':'1e4c35233e50840483467e8d6cfe556c',
'_errors':None,
'_fields':{'csrf_token ':< wtforms.ext.csrf.fields.CSRFTokenField object at 0x2207290> ;,
'dob':< wtforms.fields.simple.TextField object at 0x2207650> ;,
'email':< flask_wtf.html5.EmailField对象在0x22074d0>,
'firstname':< wtforms.fields.simple.TextField对象在0x2207350> ;,
'gender':< wtforms.fields.core.RadioField对象0x2207590>,
'languages':< wtforms.fields.core.SelectMultipleField object at 0x2207710> ;,
'lastname':< wtforms.fields.simple.TextField object at 0x2207410>},
'_prefix':u'profilefield-',
'csrf_enabled':True,
'csrf_token':< wtforms.ext .csrf.fields.CSRFTokenField对象在0x2207290> ;,
'dob':< wtforms.fields.simple.TextField对象在0x2207650> ;,
'email':< flask_wtf.html5.EmailField对象在0x22074d0>,
'firstname':< wtforms.fields.simple.TextField object at 0x2207350> ;,
'gender':< wtforms.fields.core.RadioField object at 0x2207590> ;,
'languages':< wtforms.fields.core.SelectMultipleField object at 0x2207710> ;,
'lastname':< wtforms.fields.simple.TextField object at 0x2207410>}
编辑:
我挖了一点点,发现错误是由于到csrf令牌丢失。但是我在html表单模板中包含了 {{signup_form.hidden_tag()}}
>。我可以在检查元素时看到生成的html中的隐藏标签,并可以看到带有散列值的csrf_token字段。那么这里有什么不对?
我用下面的函数解决了我的问题:
def __init __(self,* args,** kwargs):
kwargs ['csrf_enabled'] = False
super(ProfileInfoForm,self) .__ init __(* args,** kwargs)
我在 ProfileInfoForm()
问题是 FormField
包含 csrf_token
字段以及实际表单,即 RegistrationForm
也包含csrf_token,所以有两个 csrf_token
这是要验证的,只有一个实际得到渲染的形式。因此,我在 ProfileInfoForm
中禁用了 csrf_token
,所以当FormField呈现时,它有 csrf_token = False
。
和 RegistrationForm
确实有 csrf_token
现在仍然可用,所以表单仍然是安全的。
我的猜测是这也需要在 FormField
以及。
供参考:由于我对FormField代码的解释,此解决方案可能是错误的。所以请纠正我,如果我在上面的解决方案是错误的。
I have created a signup form using wtforms. I am using FormField in it so that I don't have to repeat some of the elements of the form again. But whenever I click on the Submit button it always give me false on validate_on_submit method invocation. Not getting why is this happening.
My form.py
is as follows:
class ProfileInfoForm(Form):
firstname = TextField('firstname', validators=
[validators.Required("Please enter First name.")])
lastname = TextField('lastname', validators=
[validators.Required("Please enter Last name.")])
email = EmailField('email', validators=
[validators.Required("Please enter your valid email.")])
gender = RadioField('gender', validators=
[validators.Required("Please select gender")],
choices=[('female', 'Female'), ('male', 'Male')])
dob = TextField('dob', validators=
[validators.Required("Please select date of birth.")])
languages = SelectMultipleField('languages', choices=[('', '')],
validators=
[validators.Required("Please select\
atleast one \
language.")])
class RegistrationForm(Form):
profilefield = FormField(ProfileInfoForm)
password = PasswordField('password',
validators=
[validators.Required("Please enter password."),
validators.Length(min=8),
validators.EqualTo('confirm_password',
message='Password and confirm\
password must match')])
confirm_password = PasswordField('confirm_password',
validators=
[validators.Required("Please enter\
confirm password.")])
tnc = BooleanField('tnc', validators=
[validators.Required("Please select Terms and \
Conditions")], default=False)
submit = SubmitField('Create My Account')
Signup
method is as follows:
@module.route('/signup', methods=['GET', 'POST'])
@handle_error
def signup():
if hasattr(g, 'user') and g.user:
# TODO: do some operations if needed else keep it blank
return redirect(url_for('index'))
else:
signup_form = RegistrationForm()
# Add choices for the user
signup_form.profilefield.languages.choices = getLanguages()
if signup_form.validate_on_submit():
firstname = signup_form.profilefield.firstname.data
lastname = signup_form.profilefield.lastname.data
email = signup_form.profilefield.email.data
password = signup_form.password.data
# confirm_password = signup_form.confirm_password.data
gender = signup_form.profilefield.gender.data
dob = signup_form.profilefield.dob.data
languages = signup_form.profilefield.languages.data
tnc = signup_form.tnc.data
payload = {'firstname': firstname, 'lastname': lastname,
'email': email, 'password': password, 'gender': gender,
'dob': dob, 'languages': languages,
'tnc': ('1' if tnc else '0')}
try:
buildApiUrl = BuildApiUrl()
response = requests.post(buildApiUrl.getUrl("user", "signup"),
data=payload)
if response.status_code == requests.codes.ok:
data = json.loads(response.text)
if 'status' in data and data['status'] != 200:
flash(data['message'], category="error")
else:
flash(data['message'] +
': Your account is created successfully! ' +
'Please login to your account!',
category="success")
return redirect(url_for('index'))
except requests.exceptions.RequestException:
flash('Internal Server side error occured', category="error")
return redirect(url_for('server_error', e='500'))
return render_template('public/index.html',
signup_form=signup_form, login_form=LoginForm())
HTML form is present on gist here
FYI: I am putting all the required fields with actual data needed. Still getting false when I call validate_on_submit(). What is wrong in my code?
EDIT: getLanguages is a method that retrieves languages from database and put in select list. This functionality is happening as expected and I can get list of languages.
Edit 2: Realize one thing here. This is happening due to FormField, since I tested by adding all the fields of ProfileInfoForm() into RegistrationForm() method, and everything worked just fine and I could signup. So some issue with the FormField or the way I am using it, but not sure where it is going wrong.
Found out that the problem is not with FormField but with my ProfileInfoForm(). It returns false always. Not yet got reason but I think I may have to write my own validation for that matter. Any thoughts?
Edit:
On dump I got following (used pprint here):
{'SECRET_KEY': '1e4c35233e50840483467e8d6cfe556c',
'_errors': None,
'_fields': {'csrf_token': <wtforms.ext.csrf.fields.CSRFTokenField object at 0x2207290>,
'dob': <wtforms.fields.simple.TextField object at 0x2207650>,
'email': <flask_wtf.html5.EmailField object at 0x22074d0>,
'firstname': <wtforms.fields.simple.TextField object at 0x2207350>,
'gender': <wtforms.fields.core.RadioField object at 0x2207590>,
'languages': <wtforms.fields.core.SelectMultipleField object at 0x2207710>,
'lastname': <wtforms.fields.simple.TextField object at 0x2207410>},
'_prefix': u'profilefield-',
'csrf_enabled': True,
'csrf_token': <wtforms.ext.csrf.fields.CSRFTokenField object at 0x2207290>,
'dob': <wtforms.fields.simple.TextField object at 0x2207650>,
'email': <flask_wtf.html5.EmailField object at 0x22074d0>,
'firstname': <wtforms.fields.simple.TextField object at 0x2207350>,
'gender': <wtforms.fields.core.RadioField object at 0x2207590>,
'languages': <wtforms.fields.core.SelectMultipleField object at 0x2207710>,
'lastname': <wtforms.fields.simple.TextField object at 0x2207410>}
Edit:
I dig little bit and found that the error is generated is due to csrf token missing. But I have included {{ signup_form.hidden_tag() }}
in my form template in html. and I can see hidden tag in html generated when I do inspect element and can see csrf_token field with hash value. So what is wrong in here?
I solved my problem with the following function:
def __init__(self, *args, **kwargs):
kwargs['csrf_enabled'] = False
super(ProfileInfoForm, self).__init__(*args, **kwargs)
I added this function in ProfileInfoForm()
The issue was FormField
includes csrf_token
field as well as Actual form, i.e., RegistrationForm
was also including csrf_token, so there were two csrf_token
which were to be verified and only one was getting rendered actually in form. So, I disabled csrf_token
in ProfileInfoForm
so when FormField rendered it, it had csrf_token = False
.
And RegistrationForm
does have csrf_token
enabled still now so the form is still safe.
My Guess is this does also required to be done in FormField
as well.
FYI: This solution might be wrong due to my interpretation of the FormField code. SO please correct me if I am wrong in above solution.
这篇关于Flask WTForms总是在validate_on_submit()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!