Home > Back-end >  Using Django DeleteView and getting a 404 after delete confirmation
Using Django DeleteView and getting a 404 after delete confirmation

Time:03-25

After clicking on "confirm" in my organism_delete.html form, I used to be redirected back to the list of organisms (organism_list.html template) as specified in the view. But now I get a 404 error instead.

Page not found (404) Request Method: GET Request URL: http://localhost:8000/library/organisms/ABC1233/delete/post?csrfmiddlewaretoken=Rdk575IEp5bbvrriJ1szlYNjmq8V1DvuYzNWEWz07s78IJSal9foHdkvxwcimIEp

Using the URLconf defined in itslibrary.urls, Django tried these URL patterns, in this order: admin/ accounts/ [name='home']
library/ organisms/ [name='organism_list'] library/ organisms/new/ [name='organism_new']
library/ organisms/ [name='organism_detail'] library/ organisms//update/ [name='organism_update']
library/ organisms//delete/ [name='organism_delete']
^media/(?P.*)$ The current path, library/organisms/ABC1233/delete/post, didn’t match any of these.

Two things that stand out to me is first that the error says it's a GET request, not a POST as the form specifies. And second is why is it trying to get to .../delete/post...?

It might be important to know that I changed my model and added "Primary Key = True" to a unique CharField, and I've been modifying the rest of the app to match that. It may not be related because I can list the organisms and I can get to the delete page, I just can't submit it.

I don't know how to debug this, it seems to be hidden behind the Django magic, any guidance will be very appreciated.

Code below:

Models.py:

#Organism
class Organism(models.Model):
    genbank = models.CharField(max_length = 10, primary_key=True, unique=True)
    genus = models.CharField(max_length = 50)
    species = models.CharField(max_length = 50)
    strain = models.CharField(max_length = 50)
    organism_sequence = models.TextField()
    created_at = models.DateTimeField(auto_now_add = True)
    fasta = models.FileField(upload_to='organism_fasta/', null=True, blank=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.SET_NULL, null=True)
    
    def __str__(self):
        return self.genus[:10]   ', '   self.species[:10]   ', '   self.strain[:10]
            
    def get_absolute_url(self):
        return reverse('organism_detail', args=[str(self.genbank)])
    
#Motif 
class Motif(models.Model):
    organism = models.ForeignKey('Organism', on_delete = models.CASCADE, related_name= "motifs")
    region_name = models.CharField(max_length = 15, choices = MOTIF_CHOICES)
    motif_sequence = models.CharField(max_length = 600)
    structure_image = models.ImageField(upload_to='structures/', blank=True, null=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.SET_NULL, null=True)
    created_at = models.DateTimeField(auto_now_add = True)
    
    def __str__(self):
        return self.region_name[:10]

app urls.py

urlpatterns = [
    path('organisms/', views.OrganismListView.as_view(), name='organism_list'),
    path('organisms/new/', views.OrganismCreateView.as_view(), name='organism_new'),
    path('organisms/<pk>', views.OrganismDetailView.as_view(), name='organism_detail'),
    path('organisms/<pk>/update/', views.OrganismUpdateView.as_view(), name='organism_update'),
    path('organisms/<pk>/delete/', views.OrganismDeleteView.as_view(), name='organism_delete'),
]

Delete View in views.py:

#Delete
class OrganismDeleteView(LoginRequiredMixin, DeleteView):
    model = Organism
    success_url = '/library/organisms'
    template_name = 'library/organism_delete.html'
    login_url = "/login"

organism_delete.html template

{% extends "base.html" %}
{% block content %}
 <form action="post">
     {% csrf_token %}
     <p>Are you sure you want to delete {{organism.genus}} {{organism.species}} {{organism.strain}}?</p>
     <p>This cannot be undone!</p>
     <input type="submit"  value="confirm"/>
 </form>
{% endblock content %}

CodePudding user response:

The method="…" of the form is "POST", not the action="…":

<form method="post">
     {% csrf_token %}
     <p>Are you sure you want to delete {{organism.genus}} {{organism.species}} {{organism.strain}}?</p>
     <p>This cannot be undone!</p>
     <input type="submit"  value="confirm"/>
 </form>

It might be better to work with reverse_lazy(…) [Django-doc] to determine the path of the success_url:

from django.urls import reverse_lazy

class OrganismDeleteView(LoginRequiredMixin, DeleteView):
    model = Organism
    success_url = reverse_lazy('organism_list')
    template_name = 'library/organism_delete.html'
    login_url = '/login'

CodePudding user response:

When you do <form action="post" ..., you are asking the form to "append post to the current URL, using a GET request, and go the endpoint with 'your data'".

More details here: https://www.w3schools.com/tags/att_form_action.asp

I am not sure how will you manage to make the page call again the same page (maybe omit action="...", but you should add (change) method="..."; from the implicit method="get", make it a method="post" instead.

Without being a django connoisseur myself, it would be nice if you could "easily" teach django to listen to the method="delete" instead of POST, as it will make your API a tad more semantic.

  • Related