1
votes

I have a FlaskForm class where I'm validating whether a field value is already in the system and raise a ValidationError if required.

def validate_username(self, username_field):
    if User.query.filter_by(username=username_field.data).first():
        raise ValidationError("This username is already taken.")

For User, I ended up actually having to do a separate Form class with different validation to enable updating the model.

def validate_username(self, username_field):
    if current_user.username != username_field.data:
        if User.query.filter_by(username=username_field.data).first():
            raise ValidationError("Cannot change username to this as it already is taken.")

But now I need to do it with another model that I can't access from the Form class as it is not tied to the user (it is dependent on the record being edited). I'm not sure how to go about doing this validation and would prefer not to have two FlaskForm classes for these models if possible.

Is there any way to apply certain validation methods to only a certain action (create vs. update) or do I have to write multiple FlaskForm classes for every model that has a field that is required to be unique?

I figured this design pattern must come up frequently but can't find a good example of handling it.

1
Could you clarify this a little bit. You are asking whether you can use one FlaskForm class per model or you need to create a separate FlaskForm for adding and editing cases for each of the models? - Nurjan
I would like to only use one FlaskForm per model, but I need different validation when creating vs. editing. I am wondering if there is a way to do this without having two FlaskForm classes for each model. Furthermore, even if I do need two FlaskForm classes per model, I'm not sure how to validate information from the existing record and compare it to new information entered into the form to determine if it has been changed using FlaskForm custom validation (does this need to be done in the view? If so, how to raise a ValidationError again after validate_on_submit() succeds?). - Dan

1 Answers

1
votes

You can use one FlaskForm per model. This is your form class for the User model:

class UserForm(FlaskForm):
    """
    Form to add or edit a user
    """

    username = StringField('username', validators=[DataRequired()])
    password = StringField('password', validators=[DataRequired()])
    ...

Then in your views.py you do this:

@app.route('/user/add', methods=['GET', 'POST'])
@login_required
def add_user():
    """
    Add a user to the database
    """

    add_user = True

    form = UserForm()
    if form.validate_on_submit():
        user = User(...)
        ...
        # store the user in the db

    return render_template('/.../user.html', action="Add",
                   add_user=add_user, form=form,
                   title="Add User")


@app.route('/user/edit/<int:id>', methods=['GET', 'POST'])
@login_required
def edit_user(id):
    """
    Edit a user
    """

    add_user = False

    user = User.query.get_or_404(id)
    form = UserForm(obj=user)

    if form.validate_on_submit():
        user.username = form.username.data
        ...
        # Here you can check whether new name of the user corresponds
        # already existing user
        ...


return render_template('/.../user.html', action="Edit",
                       add_user=add_user, form=form,
                       user=user, title="Edit User")

And this is how your template for user.html can look like:

<div class="...">
    {% if add_user %}
        <h1>Add User</h1>
    {% else %}
        <h1>Edit User</h1>
    {% endif %}
    <br/>
    <form action="" method="post" class="form">
      {{ form.hidden_tag() }}
      {{ wtf.form_errors(form, hiddens="only") }}
      {{ wtf.form_field(form.username) }}
      {{ wtf.form_field(form.password) }}
      ...

      <div class="form-group">
        <input type="submit" value="Submit" class="btn btn-default">
        <a href="{{ url_for('app.list_users') }}" class="btn btn-default">
          Back 
        </a>
      </div>
    </form>
</div>

You user a boolean add_user to differentiate between adding or editing an existing user. Probably there is no need to write a separate validator for this.

Hope this helps.