I'm getting a NoReverseMatch error with my delete view - python

I'm getting a NoReverseMatch error with my delete view. I think it is with my success url in my views.py but I don't know what to do I even put a kwargs with a pk key in the reverse lazy but still won't work.
Views.py:
from django.shortcuts import render
from django.views import generic
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.contrib import messages
from . import forms
from . import models
# Create your views here.
class AnnouncementListView(LoginRequiredMixin, generic.ListView):
model = models.Announcement
class AnnouncementDetailView(LoginRequiredMixin, generic.DetailView ):
model = models.Announcement
class AnnouncementUpdateView(LoginRequiredMixin, generic.UpdateView):
model = models.Announcement
form_class = forms.AnnouncementForm
class AnnouncementCreateView(LoginRequiredMixin, generic.CreateView ):
model = models.Announcement
form_class = forms.AnnouncementForm
class AnnouncementDeleteView(LoginRequiredMixin, generic.DeleteView ):
model = models.Announcement
success_url = reverse_lazy('announcement:single')
def delete(self, *args, **kwargs):
messages.success(self.request, "Post Deleted")
return super().delete(*args, **kwargs)
announcement_confirm_delete.html:
{% extends 'base.html' %}
{% block content %}
<div class="container">
<form method="post" action="{% url 'announcement:destroy' announcement.pk %}">
{% csrf_token %}
<h3>Are you sure you want to delete this?</h3>
<div class="row">
<input class='btn btn-danger' type="submit" value="Delete" />
Cancel
</div>
</form>
</div>
{% endblock %}

As the screenshot on your other question shows, announcement:single requires a pk argument, so you'd need to use a function to support that in a success_url.
Django's generic views have a get_success_url;
def get_success_url(self):
return reverse_lazy('announcement:single', kwargs={'pk': self.object.id})
However that's on a delete view, so on success you can't go to a view detailing the object you've just deleted, so it'd make more sense to go to the list view on deletion success.

Your succes ulr of AnnouncementDeleteView must be changed, now it's:
success_url = reverse_lazy('announcement:single')
I think it must be: success_url = reverse_lazy('announcement:list')
or add primary key: reverse_lazy('announcement:single', kwargs={'pk': desired_id})

Related

Show other contents besides a form in FormView?

I really dislike django CBV design which makes things without flexibility.
I would like to have a page whose upper part showing the content of objects and lower part has a form to be posted.
CBS formview
class EditStudent(FormView):
template_name = "editstudent.html"
model = models.Student
success_url = "/home"
Update:
I add the method but the error
'NoneType' object is not callable shows up.
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['students'] = Student.objects.all()[:5]
return context
How can I retriev objects of studens and show them on the template.
Thanks.
'NoneType' object is not callable: I get this error when I don't specify a form class. Created form class 'StForm' associated with model 'Student'.
In the EditStudent view class, the CreateView class was inherited, since the data was not saved to the database with the FormView.
Replace bboard with the name of the folder where your templates are placed.
I have this: templates/bboard which are in the application folder.
template_name = 'bboard/tam_form.html'
The success_url row specifies a path based on the path name.
success_url = reverse_lazy('student')
The five most recent records are also transmitted in the context.
context['students'] = Student.objects.order_by('-pk')[:5]
In the template, the first five records are displayed on top and a form is displayed below to fill out.
forms.py
from django.forms import ModelForm
from .models import Student
class StForm(ModelForm):
class Meta:
model = Student
fields = '__all__'
views.py
from .models import Student
from django.views.generic.edit import CreateView
from django.urls import reverse_lazy
from .forms import StForm
class EditStudent(CreateView):
template_name = 'bboard/editstudent.html'
form_class = StForm
success_url = reverse_lazy('student')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['students'] = Student.objects.order_by('-pk')[:5]
return context
urls.py
from django.urls import path
from .views import *
urlpatterns = [
path('student/', EditStudent.as_view(), name='student'),
]
editstudent.html
<h4>
{% for aaa in students %}
<p>{{ aaa }}</p>
{% endfor %}
</h4>
<h2>form</h2>
<form method="post" action="{% url 'student' %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="adding">
</form>

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?
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.

Django simple forum project not displaying modles on page

I have been working on a simple forums application for a few weeks now. The main goal of the project is to have sooner written a post and then for the post to be displayed. The project does not utilize user models. For some reason when the user completes the form for their post their post is not displayed. I was wondering if anyone knew why this is happening or if any of y'all have any tips.
views.py
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views import generic
from . import forms
# Create your views here.
class ForumForm(generic.CreateView):
template_name = 'forums_simple/forum.html'
form_class = forms.ForumForm
success_url = '/'
def form_vaild(self, form):
self.object = form.save(commit=False)
self.object.save()
return super().form_vaild(form)
urls.py
from django.contrib import admin
from django.urls import path, include
from . import views
app_name = 'forums'
urlpatterns = [
path('', views.ForumForm.as_view(), name='forum')
]
models.py
from django.db import models
# Create your models here.
class Post(models.Model):
message = models.TextField(blank=True, null=False)
created_at = models.DateTimeField(auto_now=True)
forms.py
from django import forms
from . import models
class ForumForm(forms.ModelForm):
class Meta:
model = models.Post
fields = ('message',)
forum.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
{% for message in object_list %}
{{ post.message }}
{% endfor %}
</div>
<div class="container">
<form class="forum_class" action="index.html" method="post">
{% csrf_token %}
{% crispy form %}
<button type="submit" name="big-button"></button>
</form>
</div>
{% endblock %}
You need to add object_list to your context in the view so:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# don't forget to import Post
context['object_list'] = Post.objects.all()
return context

I am using CreateView in django but getting error- The view blog.views.PostCreateView didn't return an HttpResponse object. It returned None instead

I am having a post model with a user having OneToMany Relationship with inbuilt user model for authentication
my urls.py
from django.contrib import admin
from django.urls import path, include
# from views import PostView
from . import views
urlpatterns = [
path('', views.PostView.as_view(), name='blogHome'),
path('post/<int:pk>/', views.PostDetailView.as_view(), name='post-detail'),
path('post/new/', views.PostCreateView.as_view(), name='post-create'),
path('about/', views.about, name='about')
]
my views.py
from django.shortcuts import render
from django.http import HttpResponse
from .models import Post
from django.views.generic import (
ListView,
DetailView,
CreateView
)
# Create your views here.
def home(request):
context = {
'posts': Post.objects.all()
}
return render(request, 'blog/home.html', context)
def about(request):
return render(request, 'blog/about.html')
class PostView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_published']
class PostDetailView(DetailView):
model = Post
class PostCreateView(CreateView):
model = Post
fields = ['title', 'body']
#to add author before validation
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
Post Model
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
body = models.TextField()
date_published = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
I am using post_form.html as the template name
post_form.html
{% extends 'blog/layout.html' %}
{% load crispy_forms_tags %}
{% block body %}
<div class="content-section p-20">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group p-30">
<legend class="border-bottom mb-4">Create new post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Create</button>
</div>
</form>
</div>
{% endblock %}
I am a beginner in Django , please tell if anything more is needed to solve the problem. Also why this type of error is coming only with Createview and not with other views
Apparently the return of super().form_valid(form) is None and not a valid response. I don't know much of this design pattern in Django but seeing your other methods seems like this view is decorated by some method which returns a valid response. So you should not return something in your implementation. So, drop the return and test again.

Django - Comment form in an existing template. How to define it in views.py?

I have 4 models: Post, Comment, Blogger and User.
I have an post_description template, in below of that, I have placed a comment form.
But how to define it in views? My problem is - to get its username, like the user who is logged in will be stored as "posted_by" and in which blog post he post will be stored as "topic" of the blog.
How to store these information, so they get automatically added?
Form that i has described in post_desc.html
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
Current view of that post_desc:
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'post_desc.html', {'post': post})
Now the user can be accessed as follows in the views:
user = request.user
And about the Topic, maybe you could add a hidden input in your form to get blog id , as you are already passing the post in the form template. :
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<input type="hidden" name="topic" value="{{ post.id }}">
<button type="submit">Submit Comment</button>
And when posted in the view you can get blog by:
post_id = request.POST.get('topic')
post = get_object_or_404(Post, pk=post_id)
And then finally proceeding with your actual flow.
I think what you need here is basic model form setup.
I am hoping there is a blog entry and comments associated with it and you want a commenting functionality on each post.
This is rough quick answer.
Your models.py looks like this:
from django.db import models
from django.conf import settings
class Comments(models.Model):
posted_by = models.ForeignKey(settings.AUTH_USER_MODEL)
topic = models.ForeignKey(Blog)
comment = models.TextField()
last_modified = models.DateTimeField(auto_now=True)
created_on = models.DateTimeField(auto_now_add=True)
You setup a model form in your forms.py
from django.forms import ModelForm
from .models import Comments
class CommentForm(ModelForm):
class Meta:
model = Comments
fields = ['comment']
You setup a model form post view.
#login_required
#require_http_methods(["POST"])
def post_comments_controller(request, identifier):
from .forms import CommentForm
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
comment_obj = comment_form.save(commit=False)
topic = Blog.objects.get(id=identifier)
comment_obj.posted_by = request.user
comment_obj.item = topic
comment_obj.save()
return HttpResponse("Done")
else:
return HttpResponseBadRequest()
You setup a entry point in your urls.py
from django.conf.urls import patterns, url
from django.conf import settings
urlpatterns = patterns('',
url(r'^/blog/(?P<identifier>[d]+)/comment$',
'views.post_comments_controller', name='post_comment')
)
And your finally the html form
{% if user.is_authenticated %}
<form method="POST" action="{% url 'post_comment' blog.id %}">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
This is not tested overall. Let me know.
From Django docs you can use FormMixin with DetailView like this:
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDetail(FormMixin, DetailView):
model = Author
form_class = AuthorInterestForm
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
# Here, we would record the user's interest using the message
# passed in form.cleaned_data['message']
return super().form_valid(form)

Categories

Resources