【问题标题】:Flask WTForms - Custom validators based of multiple form fieldsFlask WTForms - 基于多个表单字段的自定义验证器
【发布时间】:2020-05-22 11:30:13
【问题描述】:

我有一个可用书籍数量的书籍表,并且我有一个输入借阅书籍和数量的借阅表。

我正在尝试为我的表单创建自定义验证器功能,如果输入的借书数量高于图书表中的可用数量,则会在表单中显示错误消息。

这是模型:

class Borrow(db.Model):
    """
    Create a Books table
    """

    __tablename__ = 'borrows'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60), index=True)
    quantity_borrow = db.Column(db.Integer)
    date_borrow = db.Column(db.DATE)
    employees_id = db.Column(db.Integer, db.ForeignKey('employees.id'))
    book_id = db.Column(db.Integer, db.ForeignKey('books.id'))

    def __repr__(self):
        return '<Borrow: {}>'.format(self.name)


class Book(db.Model):
    """
    Create a Books table
    """

    __tablename__ = 'books'

    id = db.Column(db.Integer, primary_key=True)
    book_name = db.Column(db.String(60), index=True,unique=True)
    author = db.Column(db.String(200), index=True)
    quantity = db.Column(db.Integer)
    department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
    employees_id = db.Column(db.Integer, db.ForeignKey('employees.id'))
    publisher = db.Column(db.String(200))
    no_of_pgs = db.Column(db.Integer)
    pbs_year = db.Column(db.Integer)
    genre_id = db.Column(db.Integer, db.ForeignKey('genres.id'), nullable=False)
    read = db.Column(db.Enum('NO', 'YES'), default='NO')

    borrows = db.relationship('Borrow', backref='book',
                                lazy='dynamic')

这是视图:

    @admin_role.route('/books/add', methods=['GET', 'POST'])
    @login_required
    def add_book():
        """
        Add a book to the database
        """
        check_admin_role()

        add_book = True

        form = BookForm()
        if form.validate_on_submit():
            book = Book(book_name=form.book_name.data,
                        author=form.author.data,
                        quantity=form.quantity.data,
                        department_id=form.department_name.data,
                        employees_id=current_user.id,
                        publisher=form.publisher.data,
                        no_of_pgs=form.no_of_pgs.data,
                        pbs_year=form.pbs_year.data,
                        genre_id=form.genre_name.data,
                        read=form.read.data)
            try:
                # add department to the database
                db.session.add(book)
                db.session.commit()
                flash('You have successfully added a new department.')
            except:
                # in case department name already exists
                flash('Error: Book name already exists.')

            # redirect to departments page
            return redirect(url_for('admin_role.list_books'))

        # load department template
        return render_template('books/book.html', action="Add",
                               add_book=add_book, form=form,
                               title="Add Book", page="books")


    @admin_role.route('/borrows/add', methods=['GET', 'POST'])
    @login_required
    def add_borrow():
        """
        Add a borrow to the database
        """
        check_admin_role()

        add_borrow = True

        form = BorrowForm()

        if form.validate_on_submit():
            borrow = Borrow(name=form.name.data,
                        book_id=form.book_name.data,
                        quantity_borrow=form.quantity_borrow.data,
                        date_borrow=form.date_borrow.data)

            # add department to the database
            db.session.add(borrow)
            db.session.commit()
            flash('You have successfully added a new borrow.')


            # redirect to borrows page
            return redirect(url_for('admin_role.list_borrows'))

        # load department template
        return render_template('borrows/borrow.html', action="Add",
                               add_borrow=add_borrow, form=form,
                               title="Add Borrow", page="borrows")

这是表格

class BookForm(FlaskForm):
    """
    Form for admin_role to add or edit a books
    """
    book_name = StringField('Book Name', validators=[DataRequired()])
    author = StringField('Author', validators=[DataRequired()])
    genre_name = SelectField(coerce=int, validators=[DataRequired()])
    quantity = IntegerField('Quantity', validators=[DataRequired()])
    department_name = SelectField(coerce=int, validators=[DataRequired()])
    publisher = StringField('Publisher', validators=[DataRequired()])
    no_of_pgs = IntegerField('Number of pages', validators=[DataRequired()])
    pbs_year = IntegerField('Publishing Year', validators=[DataRequired()])
    read = SelectField("Read", choices=[(e, e) for e in Book.read.property.columns[0].type.enums])
    submit = SubmitField('Submit')

    def __init__(self):
        super(BookForm, self).__init__()
        self.department_name.choices = [(c.id, c.name) for c in Department.query.all()]
        self.genre_name.choices = [(g.id, g.name) for g in Genre.query.all()]


class BorrowForm(FlaskForm):
    """
    Form for admin_role to add or edit a books
    """
    name = StringField('Borrower Name', validators=[DataRequired()])
    book_name = SelectField(coerce=int, validators=[DataRequired()])
    quantity_borrow = IntegerField('Quantity Borrow', validators=[DataRequired())
    date_borrow = DateField('Borrow Date', validators=[DataRequired()])

    submit = SubmitField('Submit')

    def __init__(self):
        super(BorrowForm, self).__init__()
        self.book_name.choices = [(c.id, c.book_name) for c in Book.query.all()]

【问题讨论】:

    标签: python forms flask sqlalchemy wtforms


    【解决方案1】:

    在 wtforms 中,您可以通过在该表单类下编写方法来验证字段。

    在此方法中,validate_ 前缀调用以命名的字段。 self 参数将是 BorrowForm 类本身,field 参数将是您使用 validate_ 调用的字段

    class BorrowForm(FlaskForm):
        ...
    
        def validate_quantity_borrow(self, field):
            if field.data > (Book.query.filter_by(id=self.book_name.data).first().quantity - sum(i.quantity_borrow for i in Borrow.query.filter_by(book_id=self.book_name.data).all()):
                raise ValidationError("More than we have")
    

    field 这是您的BorrowFormquantity_borrow 属性。提交后,您需要获取其数据,以便field.data 为您提供输入的数量。之后,您将通过使用book_id 作为self.book_name.data 过滤数据库来选择书籍,这给了您self 作为BorrowForm 类本身和它的book_name 属性,然后是该属性的data这是所选书籍的 id。

    你需要导入你的数据库模型,比如说你的 forms.py 文件,你还需要导入ValidationError:

    from wtforms.validators import ValidationError
    

    【讨论】:

    • 这将为我提供书籍总数,但我需要检查可用书籍的数量。这意味着如果field.data &gt; Book.query.filter_by(id=self.book_name.data).first().quantity - sum of all quantity_borrow in Borrow table.我试图通过使用borrow_qty = Borrow.query.filter_by(book_id=self.book_name.data).all() total = sum(borrow_qty.quantity_borrow) 获得 quantity_borrow 的总和,但我收到错误 AttributeError: 'list' object has no attribute 'quantity_borrow' 如何从借表中获取 quantity_borrow 的总和 where book_id = self .book_name.data
    • 可以像book clss一样实现借书数量。通过查询借阅图书,然后根据book_id计算可用图书数量
    • 我不知道我该怎么做。你能给我举个例子吗?我尝试了几种解决方案,但每次这样做我都会收到 TypeError: 'int' object is not iterableAttributeError: 'list' object has no attribute 'quantity_borrow'跨度>
    • 您应该迭代所有结果并将借用的数量相加:sum(i.quantity_borrow for i in borrow_qty)。您使用 .all() 的查询会返回一个列表。您可以遍历该列表,然后达到该列表中每个项目的借用数量
    • 我也在更新计算借入数量的答案
    猜你喜欢
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 2018-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-13
    相关资源
    最近更新 更多