Using Django DeleteView and getting a 404 after delete confirmation - python

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" class="btn btn-danger" value="confirm"/>
</form>
{% endblock content %}

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" class="btn btn-danger" 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'

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

Django Python: Image Upload is not working not working

I am working on a Social Media Website, the base code is from an Open Source Project on GitHub.
On the base project you can only post text.
I was looking around the Internet and found a instruction to implement img upload.
But it doesn't work, because i get a lot of error codes.
Error Code:
AttributeError: module 'django.forms' has no attribute 'Post'
my Code:
forms.py
from django import forms
from django.forms import fields
from .models import Post, Comment
class Post(forms.Post):
class Meta:
model = Post
fields = ['name', 'Main_Img']
posts.py
""" Home page with all posts """
def first(request):
context = {
'posts':Post.objects.all()
}
return render(request, 'blog/first.html', context)
""" Posts of following user profiles """
#login_required
def posts_of_following_profiles(request):
profile = Profile.objects.get(user = request.user)
users = [user for user in profile.following.all()]
posts = []
qs = None
for u in users:
p = Profile.objects.get(user=u)
p_posts = p.user.post_set.all()
posts.append(p_posts)
my_posts = profile.profile_posts()
posts.append(my_posts)
if len(posts)>0:
qs = sorted(chain(*posts), reverse=True, key=lambda obj:obj.date_posted)
paginator = Paginator(qs, 50)
page = request.GET.get('page')
try:
posts_list = paginator.page(page)
except PageNotAnInteger:
posts_list = paginator.page(1)
except EmptyPage:
posts_list = paginator.page(paginator.num_pages)
return render(request,'blog/feeds.html',{'profile':profile,'posts':posts_list})
urls.py
urlpatterns = [...]
if settings.DEBUG:
urlpatterns += static (settings.MEDIA_URL,
document_root = settings.MEDIA_ROOT)
models.py
""" Post model """
class Post(models.Model):
title = models.CharField(max_length=150)
content = RichTextField(blank=True, null=True)
Main_Img = models.ImageField(upload_to='media/images/')
date_posted = models.DateTimeField(default=timezone.now)
date_updated = models.DateTimeField(auto_now=True)
author = models.ForeignKey(User, on_delete=models.CASCADE)
likes = models.ManyToManyField(User, related_name="blogpost", blank=True)
saves = models.ManyToManyField(User, related_name="blogsave", blank=True)
def total_likes(self):
return self.likes.count()
def total_saves(self):
return self.saves.count()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={"pk":self.pk})
post_form.py
<div class="m-auto w-100 container">
<div class="content-section">
<form method = "post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Upload</button>
</form>
</div>
</div>
This is the Website i got the code for the Img Upload from:
bit.ly/3IAKNQK
I have issues to implement them booth.
My problem is to sync the code from the website to my already existing code.
The problem is that there is no class named Post in django.forms module.
I am not sure what example you are refering to as https://de.acervolima.com/python-bilder-in-django-hochladen returns 404 not found, but I guess you replaced some class name in the example.
In Django, file upload can be implemented using FileField in django.forms
These Docs might help.
https://docs.djangoproject.com/en/4.0/topics/http/file-uploads/
https://docs.djangoproject.com/en/4.0/ref/forms/api/#binding-uploaded-files

How to pass data from Django Template to View

I have a template where customer details are displayed, along with a 'Create Lead' button at the bottom. This should take the user to the Lead creation form page where the customer field should be pre-selected.
I'm new to django. Based on responses of previous similar questions, I came up with the below code. But when I click the 'Create Lead' button, the url changes from "http://127.0.0.1:8000/sales/customer/21" to "http://127.0.0.1:8000/sales/customer/21/?customer_id=21" but nothing happens on the page. I tried with POST method and csrf token also, but it gives HTTP ERROR 405. Could some please help here.
Also, I've separate view for creating a Lead, which is sort of duplication of CreateView for Lead. And I believe that's not how it's supposed to be. What is the way to club them both in single view.
Below are the code snippets.
Models.py
class Customer(models.Model):
name = models.CharField(max_length=256)
email = models.EmailField(unique=True)
phone = models.PositiveIntegerField()
class Lead(models.Model):
customer = models.ForeignKey(Customer,related_name='Leads',on_delete=models.PROTECT)
destinations = models.CharField(max_length=256)
lead_source = models.CharField(max_length=256,choices=lead_source_choices,default='FNF')
lead_source_id = models.CharField(max_length=25,blank=True)
lead_status = models.CharField(max_length=25,choices=lead_status_choices,default='NEW')
remarks = models.TextField(blank=True)
trip_id = models.CharField(max_length=10,editable=False,unique=True,default="IN"+uuid.uuid1().hex[:5].upper())
creation_date = models.DateField(auto_now=True)
Forms.py
class LeadForm(forms.ModelForm):
class Meta:
model = Lead
fields = ('customer','destinations','lead_source','lead_source_id','lead_status','remarks')
class LeadFromCustomerForm(forms.ModelForm):
class Meta:
model = Lead
fields = ('destinations','lead_source','lead_source_id','lead_status','remarks')
Template
<form method="GET">
<input type="hidden" name="customer_id" value="{{customer.id}}">
<a href="{% url 'SalesApp:lead_create_from_customer' %}">
<button class="btn btn-warning btn-lg float-right" type="submit">Create Lead</button></a>
</form>
Urls.py
path('lead/create/',views.LeadCreateView.as_view(),name='lead_create'),
path('lead/create/customer/',views.LeadCreateFromCustomerView.as_view(),name='lead_create_from_customer')
Views.py
class LeadCreateView(LoginRequiredMixin,UserPassesTestMixin,CreateView):
form_class = LeadForm
model = Lead
def test_func(self):
return self.request.user.groups.filter(name='Sales').exists()
class LeadCreateFromCustomerView(LoginRequiredMixin,UserPassesTestMixin,CreateView):
form_class = LeadFromCustomerForm
model = Lead
def test_func(self):
return self.request.user.groups.filter(name='Sales').exists()
def form_valid(self,form):
customer_id = self.request.GET.get("value")
form.instance.customer = Customer.objects.get(id=customer_id)
return super(LeadCreateFromCustomerView,self).form_valid(form)

Problems with passing data from view to template?

I am new to Django Python and I am learning how to use Django and passing data from view to template. Now, here is my situation and I really need some help in understanding where i can be doing wrong.
I am trying to pass data from view to template and then parse the object in the view but for some reason nothing is happening in the template. I have printed the registration object in my views.py and it works fine and displays the information that is correct. But when I send the registration object from views to template nothing happens.
models.py
from django.db import models
from datetime import datetime
from django.shortcuts import redirect
# Create your models here.
# Create your models here.
class Registration(models.Model):
first_name = models.CharField(max_length=255, null=True, blank=True)
last_name = models.CharField(max_length=255, null=True, blank=True)
email = models.CharField(max_length=255, null=True, blank=True)
password = models.CharField(max_length=255, null=True, blank=True)
mobilenumber = models.CharField(max_length=255, null=True, blank=True)
created_on = models.DateTimeField(auto_now_add=True, blank=True)
class Meta:
ordering = ('first_name',)
views.py
class Loginview(CreateView):
model = Registration
form_class = LoginForm
template_name = "loginvalentis/valentis_login.html"
def get(self, request):
form = LoginForm()
# returning form
return render(request, 'loginvalentis/valentis_login.html', {'form': form});
def form_valid(self,form):
user_email = form.cleaned_data.get('email')
user_password = form.cleaned_data.get('password')
try:
registration = Registration.objects.get(email=user_email)
print ("registration",registration.mobilenumber)
return redirect('/loginvalentis/home/',{'registration':registration})
except Registration.DoesNotExist:
user_info = None
return redirect('/loginvalentis/login/')
Template result.html --- ('/loginvalentis/home/')
<html>
<body>
<form id="form1">
{% csrf_token %}
<div>
hello world
<form id ="form1">
<ul>
{% for user in registration %}
<li>{{ user.mobilenumber }}</li>
{% endfor %}
</ul>
</form>
</div>
</form>
</body>
</html>
Your problem is with the redirect() function. You are trying to pass the registration object through it, but it doesn't support this, it's *args and **kwargs are simply a parameters for reversing the url, see here:
https://docs.djangoproject.com/en/2.0/topics/http/shortcuts/#django.shortcuts.redirect
You should use some other way to pass it to another view, e.g. pass only it's id as a parameter of that view's url (you will have to change the url conf appropriatelly), the other way is to use sessions etc.
see:
https://docs.djangoproject.com/en/2.0/topics/http/sessions/
https://docs.djangoproject.com/en/2.0/topics/http/urls/
But really, it would be much easier for you just to walk through this tutorial very carefully
https://docs.djangoproject.com/en/2.0/intro/tutorial01/ trust me, it will be really worth your time, because from your question I can easily tell that you just don't understand what you are doing.

Django: path doesn't find the right primary key in url path

I'm using django2 and I get an error when I access this url:
http://127.0.0.1:8000/hotes/12/access/7/update
I get an error 404 "None access object was found"
To make a long story short:
I want to update an object linked to another. To do so, I have to send through the link, both primary keys (12 and 7 in the url). Also, I use the generic view "UpdateView" given by Django.
This is the path concerned in my project.urls:
urlpatterns = [
path('hotes/<int:pk>/access/<int:access_pk>/update/',views.AccessUpdateView.as_view(), name='access_update'),
path('hotes/add',views.host_add, name='host_add'),
path('hotes/<int:pk>/', include([
path('edit',views.HostUpdateView.as_view(), name='host_update'),
path('delete',views.host_delete, name='host_delete'),
])),
path('hotes/<int:pk>/add/', include([
path('access',views.access_add, name='access_add'),
path('oncall',views.onCall_add, name='onCall_add'),
path('network',views.network_add, name='network_add'),
])),
path('hotes/<int:pk>/',views.host_view, name='host_view'),
path('hotes/',views.hosts_view, name='hosts_view'),
path('', views.home, name='home'),
path('admin/', admin.site.urls),
]
I want the second primary key to be used in my view "AccessUpdateView".
This is a part of my models.py:
class Host(models.Model):
name = models.CharField(max_length=30, unique=True)
usage = models.CharField(max_length=30, blank=True)
function = models.CharField(max_length=30, blank=True)
production = models.NullBooleanField(blank=True, null=True)
place = models.CharField(max_length=30, blank=True)
type = models.CharField(max_length=30, blank=True)
processor = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
storage = models.CharField(max_length=10, blank=True)
memory = models.CharField(max_length=10, blank=True)
dns_inner = models.CharField(max_length=50, blank=True)
dns_extern = models.CharField(max_length=50, blank=True)
os = models.ForeignKey(Os, null=True, related_name='hosts', on_delete=models.SET_NULL, blank=True)
class Access(models.Model):
service = models.CharField(max_length=20)
client_access = models.NullBooleanField(blank=True, null=True)
ip = models.GenericIPAddressField()
login = models.CharField(max_length=30, blank=True)
password = models.CharField(max_length=50, blank=True)
host = models.ForeignKey(Host, related_name='access', on_delete=models.CASCADE)
As you can see on host can have multiple access but an access in linked to only one host.
This is the view concerned:
class AccessUpdateView(UpdateView):
model = Access
fields = ('service','client_access','ip','login','password', )
template_name = 'access_update.html'
pk_url_kwarg = 'access_pk'
context_object_name = 'access'
def form_valid(self, form):
access = form.save(commit=False)
host_id = self.kwargs['pk']
access.host_id = host_id
access.save()
return redirect('host_view', pk=host_id)
EDIT: new error when I try to access the url:
NoReverseMatch at /hotes/12/access/7/update/
Reverse for 'host_view' with arguments '('',)' not found. 1 pattern(s) tried: ['hotes\/(?P[0-9]+)\/$']
EDIT:
The error was coming from "access_update.html"
I removed the href in the Hote link which contained {% url host.pk %}
{% extends 'base.html' %}
{% load widget_tweaks %}
{% block title %}Modifier Acces{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item">Hotes</li>
<li class="breadcrumb-item">Hote</li>
<li class="breadcrumb-item active">Modification Acces</li>
{% endblock %}
{% block contenu %}
<form method="post" novalidate>
{% csrf_token %}
{% include 'includes/form.html' %}
<button type="submit" class="btn btn-success">Modifier</button>
</form>
{% endblock %}
The question is what is the right way to write the pk of the host in the url?
(host_id doesn't work)
If you want to use access_pk, then you should set pk_url_kwarg = 'access_pk' in the view.
In your form_valid method, you are using host without defining it. If pk from the URL is the host id, then you can access it with self.kwargs['pk'].
def form_valid(self, form):
access = form.save(commit=False)
host_id = self.kwargs['pk']
access.host_id = host_id
access.save()
return redirect('host_view', pk=host_id)
Inside the template for the AccessUpdateView, you have access to access since that is the object that is being updated. If you want to use the host or its id, you should access it via access.
{% url 'host_view' access.host_id %}

Where is the data created from my form going?

I just created a form for the first time and have some questions regarding the process and where the data is going.
Here are my models, views, forms, urls, and templates files;
The model from models.py:
class Member(models.Model):
member_id = models.SlugField(max_length=10)
name = models.CharField(max_length=200)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
mobile = models.SlugField(max_length=20)
income = models.CharField(max_length=200, choices=INCOME_CHOICES)
education = models.CharField(max_length=200, choices=EDUCATION_CHOICES)
home_district = models.CharField(max_length=200, choices=DISTRICT_CHOICES)
family_spending = models.CharField(max_length=200, choices=FAMILY_SPENDING_CHOICES)
children_spending = models.CharField(max_length=200, choices=CHILDREN_SPENDING_CHOICES)
birth_date = models.DateTimeField('Birthday', blank=True)
comments = models.CharField(max_length=300, blank=True)
def __str__(self):
return self.name
views.py:
def create_a_member_form(request):
if request.method == 'POST':
form = MemberForm(request.POST)
if form is valid():
member_form = form.save()
return HttpResponseRedirect('/complete/')
else:
form = MemberForm()
return render(request, 'member_form.html', {'form': form})
forms.py:
from .models import Member
from django import forms
class MemberForm(forms.ModelForm):
class Meta:
model = Member
fields = '__all__'
urls.py:
urlpatterns = [
url(r'^member_form/$', views.create_a_member_form, name='member_form')
]
The template (member_form.html):
{% load staticfiles %}
<form action="/admin/" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I want to know:
In the template, what does the /admin/ in <form action="/admin/" method="post"> represent? It's where the page redirects to after I click 'Submit', right?
Does the name='member_form' in urls.py represent the name of the HTML template the URL will match to, in thise case member_form.html?
Where is the data created from the form going? I've tried creating Member objects using the form but the new objects do not show up in my admin site under Members (while existing ones do). How do I make sure the objects created from this form do show up in my Admin site under Members?
Thank you.
Yes.
No, it's the name you use in a {% url %} tag if you want to generate a link pointing at that URL. The template is determined by the view itself (in render(request, 'member_form.html',...)).
It's not going anywhere, because your view is posting to /admin/ instead of /member_form/; /admin/ is the index of the admin site which has no code to actually accept your form data.
Note that 1 is basic HTML, and 2 and 3 are basic Django concepts which are covered in the tutorial; you should go and read that.

Categories