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.
Related
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})
Why does Django show this error: 'Forbidden (403)CSRF verification failed. Request aborted.' when I already have {% csrf_token %} in the form.
templates/core/signup.html
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Sign up</button>
</form>
{% endblock %}
views.py
from django.contrib.auth.forms import UserCreationForm
from django.views.generic.edit import CreateView
class SignUpView(CreateView):
template_name = 'core/signup.html'
form_class = UserCreationForm
Since you are already passing on the csrf token from django.core.context_processors.csrf to the context manager. Check whether the form HTML has something like this or not:
<input type='hidden' name='csrfmiddlewaretoken' value="jqhdwjavwjagjzbefjwdjqlkkop2j3ofje" />
A couple of other things are required to make the csrf protection work (check out the docs):
Your browser has to accept cookies from your server
Make sure you have 'django.middleware.csrf.CsrfViewMiddleware' included as middleware in your settings.py (alternatively use the decorator csrf_protect() on particular views you want to protect)
In your views.py you need to pass the RequestContext in your render_to_response for the context processors to actually be run.
from django.template import RequestContext
context = {}
return render_to_response('my_template.html',
context,
context_instance=RequestContext(request))
the new render shortcut (django 1.3+) will do it for you:
from django.shortcuts import render
context = {}
return render(request, 'my_template.html', context)
For class-based view:
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
# <process form cleaned data>
return HttpResponseRedirect('/success/')
return render(request, self.template_name, {'form': form})
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)
I have tried all I can gather from the forums, still need help:
I keep getting the CSRF token missing or incorrect error when I submit a form. It used to be working fine and then I made some changes and now I cant get back.. I am using {% csrf_token %} and RequestContext. I have tried using reverse, i checked the settings.py middleware for the csrf middleware, tried restarting the server, tried using HttpResponse instead of HttpResponseRedirect and template.render(), tried a url path instead of the {% url %} tag. In other parts of my project I am not even using RequestContext and it works fine..
signup_page.html:
<p>Sign Up Below
</p>
<form action={% url 'signup_page' %} method="post">
{% csrf_token %}
....
Email
<input type="email" name="email" required="true"><br><br>
<input type="submit" value="POST">
</form>
views.py
def signup_page(request):
template = loader.get_template('user_app/signup_page.html')
if request.method == "POST":
...
email = request.POST['email']
kwargs = {
'username':username, 'password':password, 'first_name':first_name,
'last_name':last_name, 'email':email
}
new_user = User.objects.create(**kwargs)
new_user.save()
context = {
'text':"POST", 'first_name':first_name
}
return HttpResponseRedirect(render('signup_page', context, context_instance =RequestContext(request)))
else:
return HttpResponse(template.render(RequestContext(request)))
urls.py:
from django.conf.urls import url
from user_app.views import signup_page, profile
urlpatterns = [
url(r'^signup', signup_page, name="signup_page"),
url(r'^profile', profile, name="profile")
]
from django.shortcuts import render
#create your views here
def your_function(request):
#do whatever you want here...
context = {'any_data': 'you_want_to_send'}
return render(request,'your.html',context)
Add csrf decorator. Reference: https://docs.djangoproject.com/en/1.10/ref/csrf/#module-django.views.decorators.csrf
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def signup_page(request):
...
I am very new to Django forms. I am trying to simply get a value from a text field and store it in a database. I am getting an error report saying:
*Forbidden (403)
CSRF verification failed.
Request aborted.
Reason given for failure:
CSRF token missing or incorrect.
For POST forms, you need to ensure:
Your browser is accepting cookies.
The view function uses RequestContext for the template, instead of Context.
In the template, there is a {% csrf_token %} template tag inside each POST form that targets an internal URL.
If you are not using CsrfViewMiddleware, then you must use csrf_protect on any views that use the csrf_token template tag, as well as those that accept the POST data.*
Where am I going wrong?
My views.py code is:
from django.shortcuts import render
from feedback.models import Test
from mysite.forms import TestForm
from django.http import HttpResponse
from django.template import Context, loader
def test_view(request):
form = TestForm
t = loader.get_template('form.html')
c = RequestContext(request,{'n':''})
if request.method=='POST':
form = TestForm(request.POST)
if form.is_valid():
in_name = request.POST.get("firstname")
fd = Test(name = in_name)
fd.save()
return HttpResponse(t.render(c))
My models.py code is:
from django.db import models
from django.forms import ModelForm
class Test(models.Model):
name = models.CharField(max_length=255)
class TestForm(ModelForm):
class Meta:
model = Test
fields = ['name']
My forms.py code is:
from django import forms
class TestForm(forms.Form):
name = forms.CharField()
My HTML template is:
<!DOCTYPE html>
<html>
<head>
<title>test form</title>
</head>
<body>
<form method = "POST">
{% csrf_token %}
First name:<br>
<input type="text" name="firstname" value = {{ n }}>
<br><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
You do it in a wrong, very PHPish, way.
Move the form definition from models.py to the forms.py, so your feedback/forms.py should be:
from django.forms import ModelForm
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ['name']
The feedback/views.py should be simplified to:
from django.shortcuts import render, redirect
from feedback.forms import TestForm
def test_view(request):
if request.method == 'POST':
form = TestForm(request.POST)
if form.is_valid():
form.save()
return redirect('.')
else:
form = TestForm()
return render(request, 'form.html', {'form': form})
And the template:
<!DOCTYPE html>
<html>
<head>
<title>test form</title>
</head>
<body>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
</body>
</html>