Search code examples
pythondjangodjango-templatesdjango-class-based-views

Django DeleteView not make delete


I want to convert user deletion from FBV to CBV

My FBV

def delete_student(request, pk):
student = get_object_or_404(Student, pk=pk)
student.delete()

return HttpResponseRedirect(reverse("students:get_students"))

My CBV

class DeleteStudentView(DeleteView):
model = Student
form_class = StudentForm
template_name = "students/students_list.html"
success_url = reverse_lazy("students:get_students")

student_list.html

 <td><a type="button" class="btn btn-danger"
                   href="{% url 'students:delete_student' student.pk %}">Delete</a>
            </td>

There is no error, but no selection occurs. What could be the problem?


Solution

  • A DeleteView deletes with a POST or DELETE request. This is mandatory by the specs of the HTTP protocol. Your FBV is not HTTP compliant.

    You thus will need to make a mini-form to do this. For example make a hidden form with:

    <td><button class="btn btn-danger" onclick="delete_item({{ student.pk }});">Delete</button></td>
    
    <!-- … -->
    
    <form method="post" action="{% url 'students:delete_student' %}" id="delete_form">
        {% csrf_token %}
        <input type="hidden" name="pk" id="delete_pk">
    </form>
    
    <script>
    function delete_item(pk) {
        var hidden_item = document.getElementById("delete_pk");
        hidden_item.value = pk;
        var form = document.getElementById("delete_form");
        form.submit();
    } 
    </script>

    In the urls.py we define an entry for the StudentDeleteView without parameters:

    # students/urls.py
    
    from django.urls import path
    
    app_name = 'students'
    
    urlpatterns = [
        # …
        path('student/delete/', StudentDeleteView.as_view(), 'delete_student'),
        # …
    ]

    In the DeleteView, you then determine the object with the primary key:

    from django.shortcuts import get_object_or_404
    
    
    class DeleteStudentView(DeleteView):
        model = Student
        success_url = reverse_lazy('students:get_students')
    
        def get_object(self, *args, **kwargs):
            return get_object_or_404(Student, pk=self.request.POST.get('pk'))

    To make your function-based view HTTP compliant, you should enforce that it can only do this with a POST or DELETE request, for example with a @require_http_methods(…) decorator [Django-doc]:

    from django.shortcuts import get_object_or_404, redirect
    from django.views.decorators.http import require_http_methods
    
    
    @require_http_methods(["DELETE", "POST"])
    def delete_student(request):
        student = get_object_or_404(Student, pk=request.POST.get('pk'))
        student.delete()
        return redirect('students:get_students')

    and thus use the same "trick" with the mini-form.