urbainy
urbainy

Reputation: 77

With Flask and JQuery, how to delete a record by "POST" method in "sexy" way?

I am using Flask to make a small web app. I need to implement CRUD operation in this app. My main problem is how to implement the "Delete" operation in a proper and "sexy" way. I can implement "Delete" operation with "GET" method originally. But after reading a bunch of documents, I found this is not a good way for sure.

So I tried some solutions to implement "Delete" operation with "POST" method and finally I am using the following code:

HTML

{% block mainbody %}
<div class="container">
    <div class="page-header">
        <h3>{{ _('User Setting') }}</h3>
    </div>

    <fieldset>
        <legend><label>{{ _('Article Category') }}</label></legend>
        <table class="col-md-offset-2 col-md-8">
            <tbody>
                {% for article_category in article_categories %}
                <tr>
                    <td>{{ article_category.name }}</td>
                    <td width="30px"><a href="{{ url_for('user.edit_article_category', id=article_category.id) }}"><span class="glyphicon glyphicon-pencil"></span></a></td>
                    <td width="30px"><a href="#" data-record-url="{{ url_for('user.delete_article_category', id=article_category.id) }}" data-record-title="{{ article_category.name }}" data-toggle="modal" data-target="#confirm-delete-modal"><span class="glyphicon glyphicon-trash"></span></a></td>
                </tr>
                {% endfor %}
                <tr>
                    <td><a href="{{ url_for('user.add_article_category') }}" class="btn" style="margin-top:10px"><span class="glyphicon glyphicon-plus"></span> {{ _('Create New Article Category') }}</a></td>
                </tr>
            </tbody>
        </table>
    </fieldset>
</div>

<div class="modal fade" id="confirm-delete-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h4 class="modal-title">{{ _('Confirm Delete') }}</h4>
            </div>
            <div class="modal-body">
                <p>{{ _('You are about to delete this record:') }} <strong><span id="deleted-content"></span></strong>, {{ _('the procedure is irreversible') }}.</p>
                <p>{{ _('Do you want to proceed?') }}</p>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-default" data-dismiss="modal">{{ _('Cancel') }}</button>
                <button type="button" class="btn btn-danger btn-ok">{{ _('Delete') }}</button>
            </div>
        </div>
    </div>
</div>

<script type=text/javascript src="{{ url_for('static', filename='js/jquery-3.1.1.min.js') }}"></script>
<script>
    $(document).ready(function() {
        var csrf_token = "{{ csrf_token() }}";
        $.ajaxSetup({
            beforeSend: function(xhr, settings) {
                if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrf_token);
                }
            }
        });
        $('#confirm-delete-modal').on('click', '.btn-ok', function(e) {
            var recordUrl = $(this).data('recordUrl');
            $.post(recordUrl, function(data) {
                console.log(data);
            });
            $('#confirm-delete-modal').modal('hide');
        });
        $('#confirm-delete-modal').on('show.bs.modal', function(e) {
            var recordTitle = $(e.relatedTarget).data('recordTitle');
            var recordUrl = $(e.relatedTarget).data('recordUrl');
            $(e.currentTarget).find('#deleted-content').text(recordTitle);
            $(e.currentTarget).find('.btn-ok').data('recordUrl', recordUrl);
        });
        $('#confirm-delete-modal').on('hidden.bs.modal', function() {
            location.reload(false);
        });
    });
</script>
{% endblock %}

Flask

@user.route("/setting/article-category/<int:id>/delete", methods=["POST"])
@login_required
def delete_article_category(id):
    deleting_article_category = ArticleCategory.get(ArticleCategory.id == id)
    deleting_article_category.delete_instance()
    return redirect(url_for("user.setting"))

The above code does work, but there are at least two drawbacks.

  1. /user/setting (url_for("user.setting")) page content is transferred twice. The first time is what the Flask code return redirect(url_for("user.setting")) makes. The second time is what the JQuery code location.reload(false) makes. It seems that the two code segments are repeat. But I cannot delete any of them. If I delete JQuery code, the /user/setting (url_for("user.setting")) page cannot get auto-refresh after I confirmed the modal and really deleted the record from the DB, which means the deleted item looks still staying there until I refresh the page manually. This is certainly inconsistent result I should avoid. If I delete Flask code return redirect(url_for("user.setting")) or change it to return Null, I will receive a 500 error from server. Although the record still can be deleted in fact, I can catch this error from the Javascript Console of Chrome. I cannot suffer this.
  2. The JQuery code location.reload(false) will download a lot of supporting stuff like bootstrap.css, bootstrap.js, logo.png, rather than the pure html file. Even I have used the false parameter to ask it try to find those stuff from the browser's cache.

Because of the above two drawbacks, I think my code's efficiency is too low. Who can help me to improve my code?

By using console.log(data), I noticed that the Flask code return redirect(url_for("user.setting")) has already caused JQuery's $.post() operation get the new page content in the feedback data. The new page content is pretty correct, which means the deleted record is not listed there any more. But there is no page refresh mechanism which can use this correct data. I think this is the key leading the low efficiency.

I am thinking Flask should provide some easy method to safely delete DB record. Just a deleting operation, which is much more complex than editing or adding. Is it because I don't know how to use Form to implement it or there is other reason to make deleting operation too hard? I am a beginner so maybe my thinking is wrong.

Is there some advice to me? Thanks!

Update for packet capture tool - Charles screen shot: enter image description here

Upvotes: 1

Views: 1767

Answers (1)

Sergey Shubin
Sergey Shubin

Reputation: 3257

Actually you don't need to send full page in return to DELETE request. Empty HTTP 204 response is the standard return value for such request:

def delete_article_category(id):
    deleting_article_category = ArticleCategory.get(ArticleCategory.id == id)
    deleting_article_category.delete_instance()
    return Response(status=204)

You don't need to worry about refreshing page as all static files are got by browser from its cache. Unless you use location.reload(true), of course. Check the logs of your application: most probably it serves static files with HTTP 304 code (Not Modified) like this:

127.0.0.1 - - [16/Mar/2017 10:10:10] "GET /static/admin/bootstrap/bootstrap3/css/bootstrap.min.css?v=3.3.5 HTTP/1.1" 304 -

Upvotes: 2

Related Questions