(Django) Delete function does not remove data from database

Viewed 793

Currently I'm having a table that shows the list of drugs. This table has a delete button in every data row. When I click it, the row does not go away.

I tried the same delete syntax (in the function called 'destroy') in Django shell and it worked. I just don't understand why when I clicked on the Remove button inside the template, nothing happened, even after refreshing.

drugs/views.py

from django.shortcuts import render, redirect
from django.views.generic import ListView
from .models import (
    Drug,
)
from .forms import (
    DrugForm,
)
def add_drug(request):
    forms = DrugForm()
    if request.method == 'POST':
        forms = DrugForm(request.POST)
        if forms.is_valid():
            drug_id = forms.cleaned_data['drug_id']
            name = forms.cleaned_data['name']
            drug_type = forms.cleaned_data['drug_type']
            Drug.objects.create(drug_id=drug_id, name=name, 
                                drug_type=drug_type)
            return redirect('drug-list')
    context = {
        'form': forms
    }
    return render(request, 'drugs/add_drug.html', context)    
def destroy(request, drug_id):  
    d = Drug.objects.get(drug_id=drug_id)  
    d.delete()  
    d.save()
    return redirect("/")     
class DrugListView(ListView):
    model = Drug
    template_name = 'drugs/drug_list.html'
    context_object_name = 'drug'

ddms/urls.py

from django.contrib import admin
from django.urls import path, include
# local
from .views import base, dashboard
from drugs.views import destroy


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', dashboard, name='dashboard'),
    path('drugs/', include('drugs.urls')),
    path('delete/<int:drug_id>', destroy), 
]

templates/drugs/drug_list.html

<div class="card-body--">
    <div class="table-stats order-table ov-h">
        <table id="bootstrapdatatable" class="table">
            <thead>
                <tr>
                    {% comment %} <th class="serial">#</th> {% endcomment %}
                    <th>ID</th>
                    <th>Name</th>
                    <th>Type</th>
                    <th>ACTION</th>
                </tr>
            </thead>
            <tbody>
                {% if drug %}
                {% for drug in drug %}
                <tr>
                    {% comment %} <td class="serial">{{ forloop.counter }}</td> {% endcomment %}
                    <td>{{ drug.drug_id }}</td>
                    <td>{{ drug.name }}</td>
                    <td>{{ drug.drug_type }}</td>
                    <td>
                        <div class="btn-group">
                            <button class="btn btn-link text-dark m-1 p-0" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                <span class="">
                                    <span id="action-button" class="fas fa-ellipsis-h icon-dark"></span>
                                </span>
                            </button>
                            <div class="dropdown-menu">
                                <a class="dropdown-item" href="#"><span class="fas fa-edit mr-2"></span>Edit</a>
                                <a class="dropdown-item text-danger" href="/delete/{{ drug.drug_id }}" data-title="Delete" data-toggle="modal" data-target="#delete"><span class="fas fa-trash-alt mr-2"></span>Remove</a>
                            </div>
                        </div>
                    </td>
                </tr>
                {% endfor %}
                {% else %}
                    <tr><td>No Drug Data</td></tr>
                {% endif %}
            </tbody>
        </table>
    </div>
</div>

drugs/models.py

from django.db import models
from .drug_types import drug_types_list


class Drug(models.Model):
    drug_id = models.CharField(max_length=20)
    name = models.CharField(max_length=50, unique=True)
    drug_type = models.CharField(max_length=20, choices=drug_types_list, default='pills')
    def __str__(self):
        return self.name

My guess is if the function in views.py is not at fault then could the problem lies inside my template, within the Remove button?

<td>
    <div class="btn-group">
        <button class="btn btn-link text-dark m-1 p-0" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
            <span class="">
                <span id="action-button" class="fas fa-ellipsis-h icon-dark"></span>
            </span>
        </button>
        <div class="dropdown-menu">
            <a class="dropdown-item" href="#"><span class="fas fa-edit mr-2"></span>Edit</a>
            <a class="dropdown-item text-danger" href="/delete/{{ drug.drug_id }}" data-title="Delete" data-toggle="modal" data-target="#delete"><span class="fas fa-trash-alt mr-2"></span>Remove</a>
        </div>
    </div>
</td>

I have just started building a project with Django and out of all CRUD templates I find this one most simplistic. Before this, I tried the module DeleteView, object.save() after deletion, checking my html,... unfortunately no error messsage popped up for this one.

Could you show me the way to solve this?

3 Answers

You should not save the drug, since then you create a new record again with the same items:

def destroy(request, drug_id):
    d = Drug.objects.get(drug_id=drug_id)
    d.delete()  
    # no d.save()
    return redirect('/')

Furthermore you should create, update and remove entities with a POST request, not a GET request: GET requests are supposed to be safe.

You can thus restrict access to the view with the @require_POST [Django-doc]:

from django.shortcuts import get_object_or_404
from django.views.decorators.http import require_POST

@require_POST
def destroy(request, drug_id):
    d = get_object_or_404(Drug, drug_id=drug_id)
    d.delete()
    return redirect('/')

and use a mini form on the HTML page instead of a link:

<form method="POST" action="/delete/{{ drug.drug_id }}/">
    <button type="submit" class="dropdown-item text-danger"><span class="fas fa-trash-alt mr-2"></span>Remove</button>
</form>

Don't save after delete object, because if you're save that data it'll create that same data in new 'id'. try this, It'll work...

def destroy(request, drug_id):  
    d = Drug.objects.get(drug_id=drug_id)  
    d.delete()
    return redirect("/") 

Combining with the first answer from @Willem Van Onsem, I modified my urls.py path to destroy function as:

path('delete/<str:drug_id>/', destroy)

with int replaced as string and included a slash / at the end of the url

Related