Search code examples
pythonpostflaskdelete-rowhttp-status-code-400

Allowing users to delete their accounts leads to 400 Bad Request in Flask


I am having problems with setting up user account deletion option. I have set up a Bootstrap modal to pop up for assurance and I want the 'Delete' button to delete the current user's account from the database, log the user out, and redirect the user to the homepage with a flash message. However, when I click the button it throws out a 400 Bad Request Error. I don't know if the reason is the POST method or something else, so here I am, asking for your help. I am using PostgreSQL along with SQLAlchemy, and Flask-Login (for the current_user) in my application. This is my first time setting up something like this, and if my question is not clear enough, please let me know so I can provide more details. Also if you have a suggestion on how can I optimize this, I would be more than grateful to hear it. Here is the related code:

The button in the modal:

<form action="{{ url_for('user.delete_account') }}" method="POST">
  <input type="submit" id="delete" name="delete" value="Delete" class="btn btn-danger">
</form>

The route and the delete function:

@user.route('/delete', methods=['POST'])
@login_required
def delete_account():
    if request.method == 'POST':
        if request.form['delete'] == 'Delete':

            current_user.delete()

            flash('Your account has been successfully deleted.', 'success')
            return redirect(url_for('core.home'))

I really hope that my question is clear enough, because I got stuck with this for more than 10 hours straight and I need the help. Thanks in advance.


Solution

  • FOUND THE SOLUTION:

    So basically, I played around a bit with the code and found the solution. Here is the relevant code:

    First, I created a Jinja2 macro with a hidden CSRF token:

    {%- macro form_tag(endpoint, fid='', css_class='', method='post') -%}
      <form action="{{ url_for(endpoint, **kwargs) }}" method="{{ method }}"
            id="{{ fid }}" class="{{ css_class }}" role="form">
        {{ form.hidden_tag() }}
        {{ caller () }}
      </form>
    {%- endmacro -%}
    

    After that I created the simplest form possible:

    from flask_wtf import Form
    from wtforms import SubmitField
    
    
    class DeleteUserForm(Form):
        delete = SubmitField('Delete')
    

    After that I added this to the Bootstrap modal:

    {% import 'macros/form.html' as f with context %}
    
    {% call f.form_tag('user.delete') %}
      <button type="submit" class="btn btn-danger">Delete</button>
    {% endcall %}
    

    At last, I modified the route in my views.py file:

    @user.route('/settings/delete', methods=['POST'])
    @login_required
    def delete():
        form = DeleteUserForm()
    
        if form.validate_on_submit():
            current_user.delete()
    
            flash('Your account has been successfully deleted. Hope to see you again.', 'success')
            return redirect(url_for('user.login'))
    
        return render_template('user/login.html', form=form)
    

    This solution was the best I could do. I hope this will help someone else too. Oh, and I almost forgot, since I was editing my settings.html file, I also had to pass the form=form argument in my /settings route of the page:

    @user.route('/settings')
    @login_required
    def settings():
        form = DeleteUserForm()
        return render_template('user/settings.html', form=form)
    

    That's all. Thanks to the ones who posted an answer too, I appreciate it.