d1spstack
d1spstack

Reputation: 1380

Delete individual SQLAlchemy row from an HTML table with flask

I've created a HTML table that lists many rows from a SQLAlchemy table. The table is created with a loop using jinja2 templates:

{% for single_merchant in merchants %}
        <tr>
            <td>{{single_merchant.id}}</td>
            <td><button type="button" class="btn btn-sm btn-outline-danger">Delete</button>
        </tr>

Each of the rows has a "delete" button. Im am trying to figure out how can I assign the delete button to delete that particular SQLalchemy row. I tried creating the button as a separate flask form called delete_form and adding an id="{{single_merchant.id}} attribute to the button like so:

{% for single_merchant in merchants %}
            <tr>
                <td>{{single_merchant.id}}</td>
                <form method="post">
                {{ delete_form.hidden_tag() }}
                <td>{{ delete_form.delete(id=single_merchant.id) }}</td>
                </form>
            </tr>

In the app.py I then created an if statement:

if delete_form.validate_on_submit():

    print(f"merchant to delete ID - {delete_form.delete.id}")

I was hoping to get the single_merchant.id in the output and use it in the if statement to delete the particular merchant from my SQLAlchemy table, but instead I got in the output merchant to delete ID - delete even though from the HTML file the id attribute had a value of 1 since {{single_merchant.id}} is 1

Is there a way how to execute SQLAlchemy row deletion from HTML tables? Here's a rough example how the HTML table is expected to look like:

enter image description here

Upvotes: 2

Views: 2979

Answers (2)

Andrew Spear
Andrew Spear

Reputation: 101

You can use your original approach as well just add a hidden field to the form that has your delete button in which the value could be your id. You just have to then explicitly define the csrf_token rather than using the hidden_tag() method.

In your WtfForm class object define a hidden field like:

class DeleteForm(FlaskForm):
    delete_id = HiddenField("Hidden table row ID")
    delete = SubmitField("Delete")

Render it in your HTML Jinja2 template like this. Notice I flipped the <td> and the <form> so that your whole table data cell is the form

{% for single_merchant in merchants %}
<tr>
    <td>{{single_merchant.id}}</td>
    <td>
        <form method="post">
            {{ delete_form.csrf_token }}
            {{ delete_form.delete_id(value=single_merchant.id) }}
            {{ delete_form.delete(class="btn btn-danger") }}
        </form>
    </td>
</tr>

Then in your code you can easily check it using the wtfform validate_on_submit() and then use the SQLAlchemy get() method to query for that id pulling the value from the hidden fields data attribute.

def post(self):
    if self.delete_form.validate_on_submit():
        entry_to_delete = YourSQLAlchemyTableClassObject.query.get(self.delete_form.delete_id.data)
        db.session.delete(entry_to_delete)
        db.session.commit()

You could if you wanted to combine the entry_to_delete line with the db.session.delete() by just putting the query to your table using the hidden field data property into the call to the delete() method. I like to separate those pieces in case I need to do anything additional to the found entry before I delete it but it doesn't matter.

Here is a screenshot of one of my apps I built for my family that allows my kids to add suggestions for our Disney Vacations. I didn't use a cool trash can icon like you did but you get the point.

enter image description here

Upvotes: 0

CodeMantle
CodeMantle

Reputation: 1426

Implement a route to handle the deletion:

def merch_delete(mid):
    merch = Merchant.query.filter_by(id=mid).first()
    if merch:
        msg_text = 'Merchant %s successfully removed' % str(merch)
        cs.delete(merch)
        cs.commit()
        flash(msg_text)
    return redirect(url_for('merchants_view'))

Then, add the following to a column in your jinja table:

<a href="{{ url_for('merch_delete', mid=single_merchant.id) }}"
  onclick="return confirm('Do you want to permanently delete merchant {{ merchant }}?');" title="Delete Merchant">
  <i class="material-icons" style="font-size:16px">delete</i></a>

The icon and Js verification step are optional.

Upvotes: 5

Related Questions