How do I debug AttributeError: 'list' object has no attribute 'match'

How do I debug AttributeError: 'list' object has no attribute 'match'

我用 flask-wtforms 创建了一个 flask 应用程序,它在点击提交后崩溃了。

错误似乎发生在 Flask-WTF function validate_on_submit 并告诉我我传递的列表对象不包含匹配属性。

现在我假设它一定是像我一样简单地错误地定义了 FormVendor 中的字段 class,但我不确定问题到底是什么。

堆栈跟踪

  File ".../webshop/vendor.py", line 38, in vendorsignup_post
    if form.validate_on_submit():

...
AttributeError: 'list' object has no attribute 'match'
...

  File ".../webshop/venv/lib/python3.6/site-packages/wtforms/validators.py", line 286, in __call__
    match = self.regex.match(field.data or '')

我的代码:

vendor.py

from flask import Blueprint, render_template, redirect, url_for, request, flash
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, FileField, HiddenField, BooleanField
from wtforms.validators import DataRequired, Email, NumberRange, Regexp, InputRequired, AnyOf

vendor = Blueprint('vendor', __name__)

class FormVendor(FlaskForm):
    per_email = StringField('Email', validators=[DataRequired(), InputRequired(),Email(message="wrong mail")])
    per_first_name = StringField('First Name', validators=[DataRequired(), InputRequired()])
    per_last_name = StringField('Last Name', validators=[DataRequired(), InputRequired()])
    adr_city = StringField('City', validators=[DataRequired(), InputRequired()])
    adr_state = StringField('State', validators=[DataRequired(), InputRequired()])
    adr_country = StringField('Country', validators=[DataRequired(), InputRequired()])
    adr_line1 = StringField('Address Line 1', validators=[DataRequired(), InputRequired()])
    adr_line2 = IntegerField('Address Line 2', validators=[DataRequired()])
    adr_postal_code = IntegerField('Postal Code', validators=[DataRequired(), InputRequired(),Regexp(['([1-468][0-9]|[57][0-7]|9[0-6])[0-9]{2}'])])
    dob_day = IntegerField('Date of Birth: Day', validators=[DataRequired(), InputRequired()])
    dob_month = IntegerField('Date of Birth: Month', validators=[DataRequired(), InputRequired()])
    dob_year = IntegerField('Date of Birth: Year', validators=[DataRequired(), InputRequired()])
    fin_account_number = StringField('IBAN Account Number', validators=[DataRequired(), InputRequired()])
    id_front = FileField('ID Front', validators=[DataRequired(), InputRequired()])
    id_back = FileField('ID Back', validators=[DataRequired(), InputRequired()])
    address_front = FileField('Address Document', validators=[DataRequired(), InputRequired()])
    accept_tos = BooleanField('I accept the TOS', validators=[DataRequired()])

@vendor.route('/vendorsignup')
@login_required
def vendorsignup():
    form = FormVendor()
    return render_template('vendorsignup.html', user=current_user, form=form)

@vendor.route('/vendorsignup', methods=['POST'])
@login_required
def vendorsignup_post():
    form = FormVendor()
    if form.validate_on_submit():
        print("Successfully registered as vendor")
        return redirect(url_for('main.sell'))
    print(form.errors)
    return render_template('vendorsignup.html', form=form)

vendorsignup.html

{% extends "base.html" %}
{% block content %}
{% if form.errors %}
    {{ form.errors }}
{% endif %}
<form method="POST" action="/vendorsignup" enctype="multipart/form-data">

    <div class="mb-3">
    {{ form.hidden_tag() }}
    {{ form.per_email.label(class='form-label') }}
    {{ form.per_email(size=20,class="form-control") }}

    {{ form.per_first_name.label(class='form-label') }}
    {{ form.per_first_name(size=20,class="form-control") }}

    {{ form.per_last_name.label(class='form-label') }}
    {{ form.per_last_name(size=20,class="form-control") }}

    {{ form.adr_city.label(class='form-label') }}
    {{ form.adr_city(size=20,class="form-control") }}

    {{ form.adr_state.label(class='form-label') }}
    {{ form.adr_state(size=20,class="form-control") }}

    {{ form.adr_country.label(class='form-label') }}
    {{ form.adr_country(size=20,class="form-control") }}

    {{ form.adr_line1.label(class='form-label') }}
    {{ form.adr_line1(size=20,class="form-control") }}

    {{ form.adr_line2.label(class='form-label') }}
    {{ form.adr_line2(size=20,class="form-control") }}

    {{ form.adr_postal_code.label(class='form-label') }}
    {{ form.adr_postal_code(size=20,class="form-control") }}

    {{ form.dob_day.label(class='form-label') }}
    {{ form.dob_day(size=20,class="form-control") }}

    {{ form.dob_month.label(class='form-label') }}
    {{ form.dob_month(size=20,class="form-control") }}

    {{ form.dob_year.label(class='form-label') }}
    {{ form.dob_year(size=20,class="form-control") }}

    {{ form.fin_account_number.label(class='form-label') }}
    {{ form.fin_account_number(size=20,class="form-control") }}

    {{ form.id_front.label(class='form-label') }}
    {{ form.id_front(class="form-control") }}

    {{ form.id_back.label(class='form-label') }}
    {{ form.id_back(class="form-control") }}

    {{ form.address_front.label(class='form-label') }}
    {{ form.address_front(class="form-control") }}

    {{ form.accept_tos.label(class='form-label') }}
    {{ form.accept_tos(class="form-control") }}
    </div>

    <input type="submit" value="Go">
</form>

{% endblock %}

在您的 adr_postal_code IntegerField 中,您的最后一个验证器如下所示:

Regexp(['([1-468][0-9]|[57][0-7]|9[0-6])[0-9]{2}'])

如果你看一下 Regexp.__init__ source code,你会发现它期望 regex 参数是一个字符串或正则表达式对象,但你传递的是一个列表,其中包含一个字符串。

这是 __init__ 的样子:

def __init__(self, regex, flags=0, message=None):
    if isinstance(regex, str):
        regex = re.compile(regex, flags)
    self.regex = regex
    self.message = message

然后,就在 __init__ 下方,您会找到 __call__ 方法,这是您的代码失败的地方。 __call__ 假设无论 self.regex 是什么类型,它都必须有一个 match 方法。由于您传入了一个列表,因此 self.regex 将是一个列表,并且没有 match 方法。

因此,您的验证器应该是:

Regexp('([1-468][0-9]|[57][0-7]|9[0-6])[0-9]{2}')