Django routing POST deletion request to viewProduct? - python

When I try to delete a product on my django app, I get a 404 error stating:
Page not found (404)
Request Method: POST
Request URL: http://localhost:8000/product/1/delete/
Raised by: products.views.viewProduct
No Product matches the given query.
I'm confused as to why Django is routing the deletion request to viewProduct, since my urls.py clearly states that it should route to deleteProduct.
urls.py:
from django.urls import path
from . import views
from django.views.generic import TemplateView
urlpatterns = [
path('new/', views.NewProduct, name='NewProduct'),
path('product/<int:pk>/<str:slug>/', views.viewProduct, name='viewProduct'),
path('product/<int:pk>/delete/', views.deleteProduct, name='deleteProduct'),
]
Views:
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
from .models import Product
from django.utils import timezone
from slugify import slugify
def viewProduct(request, pk, slug):
product = get_object_or_404(Product, pk = pk, slug = slug)
return render(request, 'viewProduct.html', {'product' : product})
def deleteProduct(request, pk):
if request.method == 'GET':
product = Product.objects.filter(pk = pk)
return redirect('viewProduct', pk = pk, slug = product.slug)
if request.method == 'POST':
product = get_object_or_404(Product, pk = pk)
if product.productAuthor == request.user:
product.delete()
return redirect('viewAll')
Deletion form:
{% if request.user == product.productAuthor %}
<form action="/product/{{ product.pk }}/delete/" method="post">
{% csrf_token %}
<input type="submit" name="pk" class="btn btn-link text-danger" value="delete product">
</form>
{% endif %}
If it helps you, I just recently changed the site to view posts by both ID and slug instead of just ID. Before I changed this, the deletion function worked perfectly.

Maybe it is the way you indent after deletion, try this
I cannot see your viewAll url handler where you want to redirect, you need to write a viewAll view where you list all your products
from django.urls import reverse
urlpatterns = [
path('',views.ViewAll, name='viewAll')
path('new/', views.NewProduct, name='NewProduct'),
path('product/<int:pk>/<str:slug>/', views.viewProduct, name='viewProduct'),
path('product/<int:pk>/delete/', views.deleteProduct, name='deleteProduct'),
]
def deleteProduct(request, pk):
if request.method == 'GET':
product = Product.objects.filter(pk = pk)
return redirect('viewProduct', pk = pk, slug = product.slug)
if request.method == 'POST':
product = get_object_or_404(Product, pk = pk)
if product.productAuthor == request.user:
product.delete()
return redirect(reverse('yourappname:viewAll'))

URL patterns are processed in order. "delete" is a perfectly valid value for a slug, so the view pattern is matched and its view called.
You could fix this by swapping the order of the URLs, so that "delete" is matched first.

Related

Django forms: cannot access local variable 'form' where it is not associated with a value

Condition: I have a model, created an empty table in the database, and I'm trying to create an html form that will fill in the fields of the corresponding columns of the table.
And here's what my app looks like:
models.py
from django.db import models
class Cities(models.Model):
city = models.CharField(max_length=100)
def __str__(self):
return self.state
class Routes(models.Model):
route_name = models.CharField(max_length=50, default='Route')
lvl = models.IntegerField(default=0)
about = models.TextField(max_length=1500)
total_distance = models.IntegerField(default=0)
city = models.ForeignKey(Cities, on_delete=models.CASCADE)
forms.py
from django.forms import ModelForm
from .models import Routes
class RouteForm(ModelForm):
class Meta:
model = Routes
fields = '__all__'
views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse
from routes_form.forms import RouteForm
def getAbout(request):
if request.method == 'POST':
form = RouteForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'routes_form/form_page.html', {'form': form})
form.html
<form method="post">
{% csrf_token %}
<legend>
<h2>About</h2>
</legend>
{{ form }}
<input type="text" placeholder="Write more about the route: about waypoints, points of interest and warnings.">
<input type="submit" value="Send route">
</form>
I have already tried to do everything as indicated in the Django Forms documentation. But still something is wrong. Even at the moment of starting the server, it writes an error:
cannot access local variable 'form' where it is not associated with a value
It is because you haven't defined form for GET method so:
def getAbout(request):
if request.method == 'POST':
form = RouteForm(request.POST)
if form.is_valid():
form.save()
return redirect('some_view_name_to_redirect')
else:
form=RouteForm()
return render(request, 'routes_form/form_page.html', {'form': form})
Note: Models in Django are written in singular form, as Django itself add s as the suffix, so it is better to name the models as City and Route.
Here you passed form = RouteForm(request.POST) object for POST request you need to pass for GET request so, when def getAbout(request) function called with GET request then renders it like this ...
def getAbout(request):
form=RouteForm() # <---- called at GET request
if request.method == 'POST':
form = RouteForm(request.POST) # <---- called at POST request
if form.is_valid():
form.save()
return redirect("/")
return render(request, 'routes_form/form_page.html', {'form': form})

Django: Problem deleting an Authenticated User profile

I'm having problems deleting a user, where the authenticated user can delete their own account.
But what happens is that the page just refreshes, in the same template and returning '200 ok from POST'
[06/Aug/2022 11:46:33] "POST /members/profile/ HTTP/1.1" 200 4998
members.views.profiles.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
#login_required(login_url="/accounts/login/")
def profile(request):
template_name = "members/profile.html"
context = {}
return render(request, template_name, context)
def profile_delete(request, pk):
user_profile = User.objects.filter(pk=pk)
template_name = "members/profile_delete.html"
context = {
"user_profile": user_profile,
},
if request.method == "POST":
user_profile.delete()
return render(request, template_name, context)
return render(request, template_name, context)
members.urls.py
from django.urls import path
from allauth.account import views as allauth_views
from . import views
app_name = "members"
urlpatterns = [
path("login/", allauth_views.LoginView.as_view(), name="login"),
path("logout/", allauth_views.LogoutView.as_view(), name="logout"),
path("profile/", views.profile, name="profile"),
path("profile/<int:pk>/delete/", views.profile_delete, name="profile_delete"),
]
profile.html
<div>
<form method="POST">
{% csrf_token %}
<p>Are you sure you want to delete <strong>{{ user | title }}</strong> ?</p>
<button class="btn btn-danger" href="{% url 'members:profile_delete' user.pk %}" type="submit">
Delete
</button>
</form>
</div>
Solution:
views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
#login_required(login_url="/accounts/login/")
def profile(request):
template_name = "members/profile.html"
context = {}
return render(request, template_name, context)
def profile_delete(request, pk):
user_profile = get_object_or_404(User, pk=pk)
user_profile.delete()
template_name = "members/profile_delete.html"
context = {}
return render(request, template_name, context)
When you're pressing your button the server sends a GET request, try changing the logic in your function, instead of:
if request.method == "POST":
Use:
if request.method == "GET":
You should use get_object_or_404 for querying single user's profile and then delete it, currently filter() makes no sense, so:
user_profile=get_object_or_404(User,pk=pk)
Note: you should always return HttpResponseRedirect after dealing with POST data, the tip is not specific to Django, it's a good web practice in general.

Django Form in ListView, error on POST of Form

Im trying to implement a chat-function for my website. In order to do that, i followed the following tutorial: https://channels.readthedocs.io/en/latest/tutorial/
I've then changed the code a little bit in order to implement it. Until here, everything works just fine. Now I want to store the form-data inside of a database, and thats where the problem appears.
But first my code:
urls.py:
from django.urls import path
from .views import ChatOverView
urlpatterns = [
path('<int:pk>/', ChatOverView.as_view(), name='chat-explicit'),
path('', ChatOverView.as_view(), name='chat-home'),
]
views.py (theres much code here that is probably not needed for this question, but since i dont know what part of it i can ignore, im just posting the whole file-content):
from django.views.generic import ListView
from django.views.generic.edit import FormMixin, FormView
from django.db.models import Q
from django.urls import resolve
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from .models import Message
from .forms import MessageRegisterForm
class ChatOverView(ListView, FormMixin):
model = Message
template_name = 'chat/home-chat.html'
form_class = MessageRegisterForm
success_url = '/thanks/'
def form_valid(self, form):
form = self.get_form()
if form.is_valid():
data = form.cleaned_data
return super().form_valid(form)
def get_context_data(self, *args, **kwargs):
context = super(ChatOverView, self).get_context_data(*args, **kwargs)
messages_raw = reversed(Message.objects.filter(Q(sender=self.request.user) | Q(receiver=self.request.user)))
messages = {}
for mes in messages_raw:
# i am receiver
if mes.sender != self.request.user:
if mes.sender in messages:
messages[mes.sender].append({"received": mes})
else:
messages.update({mes.sender: [{"received": mes}]})
# i sent
else:
if mes.receiver in messages:
messages[mes.receiver].append({"sent": mes})
else:
messages.update({mes.receiver: [{"sent": mes}]})
active_user = self.get_active_chat(messages)
chatroom_name = self.get_chatroom_name(active_user)
context.update(messages_data=messages, active=active_user, roomname=chatroom_name)
return context
def get_chatroom_name(self, active_chat):
# my convention
ids = [active_chat.id, self.request.user.id]
ids.sort()
return str(ids[0]) + '_' + str(ids[1])
def get_active_chat(self, messages):
url_name = resolve(self.request.path_info).url_name
if url_name == "chat-home":
return list(messages.keys())[0]
else:
pk_user = self.request.build_absolute_uri().split("/")[-2]
user = get_object_or_404(User, pk=pk_user)
return user
home-chat.html:
{% extends "solawi/base.html" %}
{% load define_dictfilters %}
{% block content %}
<form class="bg-light" method="post">
<div class="input-group">
{% csrf_token %}
{{ form }}
<div class="input-group-append">
<button type="submit" id="chat-message-submit" value="enter">send</button>
</div>
</div>
</form>
{% endblock content %}
models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Message(models.Model):
sender = models.ForeignKey(User, related_name="sender", on_delete=models.CASCADE)
receiver = models.ForeignKey(User, related_name="receiver", on_delete=models.CASCADE)
content = models.TextField()
date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.sender.username + ' to ' + self.receiver.username
The rest of the files/settings entrys are the same as in the tutorial linked above.
Now comes the problem: when submitting the form, i get the following error:
Method Not Allowed (POST): /chat/
Method Not Allowed: /chat/
HTTP POST /chat/ 405 [0.00, 127.0.0.1:54424]
How can i fix it?
Thank you for your Help!!
ListView implements a get() method, but no post() method. You need to implement a post() method in order for the view to allow POST requests.
You could subclass django.views.generic.ProcessFormView to get this, or, if you really need a ListView, then you can add a post() method to the class which handles the form validation and whatever else you need to do. Here is how ProcessFormView implements it:
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)

Django NoReverseMatch at /

I am trying to add user uploadable images in my web app using Pillow. I created a Django Upload model and registered it in Admin. As soon as I added a photo using admin console I get the following error. Initially the website was working all fine
The Error
NoReverseMatch at /
Reverse for 'thing_detail' with arguments '()' and keyword arguments '{u'slug': ''}' not found. 1 pattern(s) tried: ['things/(?P<slug>[-\\w]+)/$']
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.8.4
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'thing_detail' with arguments '()' and keyword arguments '{u'slug': ''}' not found. 1 pattern(s) tried: ['things/(?P<slug>[-\\w]+)/$']
Exception Location: /usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py in _reverse_with_prefix, line 496
Python Executable: /usr/bin/python
Python Version: 2.7.6
Python Path:
['/home/shashank/development/hellowebapp/hellowebapp',
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-x86_64-linux-gnu',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages/PILcompat',
'/usr/lib/python2.7/dist-packages/gtk-2.0',
'/usr/lib/pymodules/python2.7',
'/usr/lib/python2.7/dist-packages/ubuntu-sso-client']
Server time: Sun, 13 Mar 2016 18:54:31 +0000
Urls.py
from collection.backends import MyRegistrationView
from django.conf.urls import include, url
from django.contrib import admin
from collection import views
from django.conf import settings
from django.views.generic import TemplateView
from django.contrib.auth.views import (
password_reset,
password_reset_done,
password_reset_confirm,
password_reset_complete
)
urlpatterns = [
url(r'^$', views.index, name='home'),
url(r'^about/$',TemplateView.as_view(template_name='about.html'),name='about'),
url(r'^contact/$',TemplateView.as_view(template_name='contact.html'),name='contact'),
url(r'^things/(?P<slug>[-\w]+)/$','collection.views.thing_detail',name='thing_detail'),
url(r'^things/(?P<slug>[-\w]+)/edit/$','collection.views.edit_thing',name='edit_thing'),
url(r'^things/(?P<slug>[-\w]+)/edit/weight$','collection.views.edit_weight',name='edit_weight'),
url(r'^things/(?P<slug>[-\w]+)/delete/weight$','collection.views.remove_weight',name='remove_weight'),
#WORKING url(r'^things/(?P<pk>\d+)/remove/$', 'collection.views.remove_weight', name='remove_weight'),
url(r'^things/$',TemplateView.as_view(template_name='weight_removed.html'),name='weight_removed'),
url(r'^(?P<slug>[\w\d-]+)/(?P<pk>\d+)/$','collection.views.remove_weight',name='remove_weight'),
#url(r'^edit/(?P<slug>\d+)/weights$', 'collection.views.AddWeight',name='AddWeight'),
# the new password reset URLs
url(r'^accounts/password/reset/$',password_reset,{'template_name':'registration/password_reset_form.html'},name="password_reset"),
url(r'^accounts/password/reset/done/$',password_reset_done,{'template_name':'registration/password_reset_done.html'},name="password_reset_done"),
url(r'^accounts/password/reset/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$',password_reset_confirm,{'template_name':'registration/password_reset_confirm.html'},name="password_reset_confirm"),
url(r'^accounts/password/done/$',password_reset_complete,{'template_name':'registration/password_reset_complete.html'},name="password_reset_complete"),
#setup additional registeration page
url(r'^accounts/register/$',MyRegistrationView.as_view(),name='registration_register'),
url(r'^accounts/create_thing/$','collection.views.create_thing',name='registration_create_thing'),
url(r'^accounts/',include('registration.backends.default.urls')),
url(r'^admin/', include(admin.site.urls)),
]
if settings.DEBUG:
urlpatterns += [
url(r'^media/(?P<path>.*)$','django.views.static.serve',{'document_root': settings.MEDIA_ROOT,}),
]
Models .py
from django.db import models
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
class Thing(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True)
user = models.OneToOneField(User, blank=True, null=True)
class Weight(models.Model):
date = models.DateTimeField(default=timezone.now)
weight_value = models.CharField(max_length=255)
thingRA = models.ForeignKey(Thing,related_name="weights")
class Meta:
order_with_respect_to = 'thingRA'
ordering = ['date']
def get_image_path(instance, filename):
return '/'.join(['thing_images', instance.thing.slug, filename])
class Upload(models.Model):
thing = models.ForeignKey(Thing, related_name="uploads")
image = models.ImageField(upload_to=get_image_path)
Admin.py
from django.contrib import admin
# import your model
from collection.models import Thing, Weight, Upload
class ThingAdmin(admin.ModelAdmin):
model = Thing
list_display = ('name', 'description',)
prepopulated_fields = {'slug': ('name',)}
# and register it
admin.site.register(Thing, ThingAdmin)
class WeightAdmin(admin.ModelAdmin):
model = Weight
list_display = ('date','weight_value',)
admin.site.register(Weight, WeightAdmin)
class UploadAdmin(admin.ModelAdmin):
list_display = ('thing', )
list_display_links = ('thing',)
# and register it
admin.site.register(Upload, UploadAdmin)
Base.html
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<title>
{% block title %}
WEB PAGE BY SHASHANK
{% endblock title %}
</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}" />
</head>
<body>
<div id="page">
<div id="logo">
<h1>S PORTAL</h1>
</div>
<div id="nav">
<ul>
<li>Home</li>
<li>About</li>
<li>Contact</li>
{% if user.is_authenticated %}
<li>Logout</li>
<li>My Profile</li>
{% else %}
<li>Login</li>
<li>Register</li>
{% endif %}
</ul>
</div>
{% block content %}{% endblock content %}
<div id="footer">
<p>
Webpage made by SHASHANK
</p>
</div>
</div>
</body>
</html>
View.py
from django.shortcuts import render,redirect,get_object_or_404
from collection.models import Thing, Weight
from collection.forms import ThingForm, WeightForm, ThingWeightFormSet
from django.template.defaultfilters import slugify
from django.contrib.auth.decorators import login_required
from django.http import Http404
from django.views.decorators.csrf import csrf_protect
from django.views.generic import ListView, CreateView, UpdateView
from django import forms
def index(request):
things = Thing.objects.all()
return render(request,'index.html',{'things':things,})
def thing_detail(request, slug):
# grab the object...
thingRA = Thing.objects.get(slug=slug)
weights = thingRA.weights.all().order_by('-date')
# and pass to the template
return render(request, 'things/thing_detail.html', {'thing': thingRA, 'weights':weights,})
def edit_thing(request, slug):
# grab the object
thing = Thing.objects.get(slug=slug)
# set the form we're using
form_class = ThingForm
# if we're coming to this view from a submitted form
if request.method == 'POST':
# grab the data from the submitted form and apply to
# the form
form = form_class(data=request.POST, instance=thing)
if form.is_valid():
# save the new data
form.save()
return redirect('thing_detail', slug=thing.slug)
# otherwise just create the form
else:
form = form_class(instance=thing)
# and render the template
return render(request, 'things/edit_thing.html', {'thing': thing,'form': form,})
def create_thing(request):
form_class = ThingForm
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
thing = form.save(commit=False)
thing.user = request.user
thing.slug = slugify(thing.name)
thing.save()
slug = slugify(thing.name)
return redirect('thing_detail', slug=thing.slug)
else:
form = form_class()
return render(request,'things/create_thing.html', {'form': form,})
def edit_weight(request, slug):
thing = get_object_or_404(Thing, slug=slug)
if request.method == "POST":
form = WeightForm(request.POST)
if form.is_valid():
weight = form.save(commit=False)
weight.thingRA = thing
weight.save()
return redirect('thing_detail', slug=thing.slug)
else:
form = WeightForm()
return render(request, 'things/edit_weight.html', {'form': form})
"""WORKING WEIGHT
def remove_weight(request, pk):
weight = get_object_or_404(Weight, pk=pk)
thing_pk = weight.thingRA.pk
weight.delete()
return redirect('weight_removed')
"""
def remove_weight(request, pk, slug):
weight = get_object_or_404(Weight, pk=pk)
thing = get_object_or_404(Thing, slug=slug)
thing_pk = weight.thingRA.pk
weight.delete()
return redirect('thing_detail', slug=slug)
#login_required
def edit_thing(request, slug):
# grab the object...
thing = Thing.objects.get(slug=slug)
# make sure the logged in user is the owner of the thing
if thing.user != request.user:
raise Http404
# set the form we're using...
form_class = ThingForm
# if we're coming to this view from a submitted form,
if request.method == 'POST':
# grab the data from the submitted form and
# apply to the form
form = form_class(data=request.POST, instance=thing)
if form.is_valid():
# save the new data
form.save()
return redirect('thing_detail', slug=thing.slug)
# otherwise just create the form
else:
form = form_class(instance=thing)
# and render the template
return render(request, 'things/edit_thing.html', {'thing': thing,'form': form,})
I would say that you have a Thing with slug="". You can check this out using django shell:
from yourapp.models import Thing
Thing.objects.get(slug='')
According to your model definition slug can be blank, but your url pattern does not accept blank slug. You will have to fix either your slug field or your url pattern.
You're probably using a {% url 'thing_detail' slug=thing.slug %} in your template with a 'thing' object that has an empty slug attribute.
Either add a conditional to your template that checks for an existing slug in the object so that the template tag isn't invoked or make sure that none of your Thing objects have a blank slug.
Finally figured it out. The problem was nowhere in the code. Actually all I needed was to logout from the admin panel in Django. Since being logged in the Admin panel prevented other Users to interact with their related data.

Form submit not POST's the value in Django

When i just tried to save data using this simple form , it is not getting posted . Is there anything wrong in declaration of actions or url's ?
Here the request.method is GET instead of POST even-though the form method is set as POST
Model file
from django.db import models
# Create your models here.
class Contact(models.Model):
name = models.CharField(max_length=30, null=True, blank=True)
company_id = models.CharField(max_length=30)
def __unicode__(self):
return self.name
Form.py uses the modelform
from contact.models import Contact
from django.forms import ModelForm
class AddcntForm(ModelForm):
class Meta:
model = Contact
Views
from contact.forms import AddcntForm
from django.contrib import messages
from django.shortcuts import render_to_response, redirect, get_object_or_404
from django.template.context import RequestContext
def add_cnt(request, form_class=AddcntForm):
print request.method
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
form.save(request)
messages.success(request, "New Contact added.")
return redirect('##success##')
else:
form = form_class()
return render_to_response(
'vec/add_cnt.html',
{'form': form},
context_instance=RequestContext(request))
Url
from django.conf.urls import *
from django.conf import settings
urlpatterns = patterns('contact.views',
url(r'^addcnt/$', 'add_cnt', name='add_cnt'),
)
template file is as follows
{% block content %}
<form method="post" action="/hr/addcnt/" >{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Ok" />
</form>
{% endblock %}
You're passing the request.GET querydict to your form when the method is POST. You should pass request.POST instead.
Also you're passing the request to form.save(). The only (optional) argument expected by ModelForm.save() is a boolean "commit" flag which, if true, prevent the form from effectively saving the instance (cf https://docs.djangoproject.com/en/1.5/topics/forms/modelforms/#the-save-method). Remember that in Python each object have a boolean value... IOW you're saying the form to not save your instance ;)
I was confused too with the same issue.
When the form is called initially it is "GET" request so the statement -print request.method will print "GET".
After entering values in the form if you click on submit, you can see in the console the same statement -print request.method will print "POST" which is actually a post request.

Categories