简体   繁体   中英

Flask WTForms - Custom validators based of multiple form fields

I have a books table where quantity of books is available and I have borrows table where I enter the borrowed books and quantities.

I am trying to create custom validator function for my form that will show error message in form if quantity entered for borrow book is higher than available quantity in book table.

this is the model:

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')

This is the view:

    @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")

And this is the form

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()]

In wtforms you can validate a field by writing a method under that form class.

In this method, validate_ prefix calls the field named after. self argument will be the BorrowForm class itself and the field argument will be the field you called with 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 here is the quantity_borrow attribute of your BorrowForm . When submitted you need to get its data so field.data gives you entered quantity. After that you will get the book chosen by filtering the database with book_id as self.book_name.data which gives you, self as the BorrowForm class itself and book_name attribute of it then, the data of that attribute which is the id of book chosen.

You need to import your database models,lets say in your forms.py file, and also you need to import ValidationError :

from wtforms.validators import ValidationError

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM