Django Simple POST Class View Not Working - python

I am experimenting with Django class based views and for some reason this simple post view does not seem to be working. When looking at my terminal it gets stuck on the GET method / validation of the csrf token. It is either not being validated or not saving to the database and therefore not being redirected to the thank-you page. I am not sure how to solve this issue. It definitely is not being saved to the DB, but as far as I am aware everything is correct here. I also am using a class based form which is why I am simply just calling form.save() The code is as follows:
class ReviewView(View):
def get(self, request):
form = ReviewForm()
return render(request, 'reviews/review.html', {
'form': form
})
def post(self, request):
form = ReviewForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/thank-you')
return render(request, 'reviews/review.html', {
'form': form
})
Any help or ideas is greatly appreciated!

Related

django comments views from scratch

I'm working on a blog build on django and doing the comment stuff and I would like to build it from scratch here my views function:
def topic_detail(request, slug):
topic = get_object_or_404(Topic, slug=slug)
form = CommentForm()
if request.method == 'POST':
if request.user.is_authenticated:
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.topic = topic
comment.created_by = request.user
comment.save()
return redirect('board:topic_detail', slug=topic.slug)
else:
redirect('accounts:login')
else:
form = CommentForm()
return render(request, 'topic.html', {'topic':topic, 'form':form})
my page layout would be:
< ............................>
Something I want to write
<.............................>
Comment Field
<.............................>
Comments
So when user presses the button, server will check if that user is authenticated. If yes comment is updated, If no user will be directed to login views. Here the problem, when I'm logged in everything works fine, but when I log out test the views, It does not redirect me to the login views but just reload the page. I would appreciate if you help me.
Thanks!
You should use return redirect(....) instead of just redirect(...) to return the actual HttpResponse. Now your code continues to the last line and renders the same page again.

Django redirect, but pass form error context

I have a very simple index page view, from which the user can fill in a login popup, which sends a post request to /login
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
context['login_form'] = LoginForm()
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
If the login is valid it simply redirects to the index page, this works fine.
The login view looks as follows:
def user_login(request):
form = LoginForm(request.POST)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context all over again
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
However when the login is incorrect I want to be able to refresh the page and show the login form errors inside the index template (in the login popup)
I'm actually able to achieve this with the above code, but I'm unhappy with the solution for the following reasons:
I have to manually fetch the context all over again, e.g user/student forms and studyspaces, this goes against the DRY principle
When the page is refreshed the url is localhost:8000/spacefinder/login
Screenshot of behaviour here
I'm wondering if there's somehow a way to use redirect to reload the index page and somehow pass errors from my login_form, e.g. something like:
return redirect('spacefinder:index', {'login_form': form})
I've looked into using messages to pass form validation errors, but struggled to get this working since Validation Errors are thrown inside forms.py, and I'm unable to fetch the request instance from inside a ModalForm to properly create a message
You are doing it the wrong way around.
Consider these prerequisites:
entry point to your page is the index view
the index view must only be accessible by authenticated users
the login view allows both methods GET and POST and is accessible to anonymous users only
The reason to use Django is to make use of all the features that it offers, and that includes handling of the above (because that is what most pages need, not only you).
To set it up correctly you need to define your urls.py like this:
from django.contrib.auth.decorators import login_required
urlpatterns = [
....
url('^login/$', user_login, 'login'),
url('^/$', login_required(index), 'index'),
....
]
In your settings/base.py (or settings.py if you have no environment differentiation) tell Django how to redirect users:
LOGIN_URL = reverse_lazy('login')
LOGIN_REDIRECT_URL = reverse_lazy('index')
https://docs.djangoproject.com/en/1.9/ref/settings/#login-url
https://docs.djangoproject.com/en/1.9/ref/settings/#login-redirect-url
Simplify your index view:
def index(request):
"""Shows list of studyspaces, along with corresponding 'busyness' score"""
context = {'study_space_list': StudySpace.objects.order_by('-avg_rating')}
if request.user.is_authenticated():
context['user'] = request.user
else:
return HttpResponseForbidden() # prevented by Django, should never happen
return render(request, 'spacefinder/index.html', context)
Let the user_login view deliver the empty login form:
#require_http_methods(["GET", "POST"])
def user_login(request):
params = getattr(request, request.method)
form = LoginForm(params)
if request.method == 'POST' and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return redirect(reverse('spacefinder:index'))
# Load the context for new form or form with errors
context = {
'study_space_list': StudySpace.objects.order_by('-avg_rating')
}
context['login_form'] = form
context['user_form'] = UserForm()
context['student_form'] = StudentForm()
return render(request, 'spacefinder/index.html', context)
You have not presented any code that handles the UserForm or the StudendForm. You would need to add that to the user_login view, as well - if this is something that all users should fill in every time they login. Otherwise use a different view.
It's worth looking at modules like allauth. They might spare you some work when it comes to allowing users to login with their e-mail addresses, ascertain that e-mail addresses are unique in the system etc.

django redirecting back to the page from where the request was made

I implemented in django return to original page via redirect see view example
#login_required
def category_edit(request, pk,uri):
post = get_object_or_404(Category, pk=pk)
if request.method == "POST":
form = CategoryForm(request.POST, instance=post)
if form.is_valid():
category = form.save(commit=False)
category.author = request.user
category.creation_time = timezone.now()
category.save()
return redirect(uri)
#return redirect('category_details', pk=post.id)
else:
form = CategoryForm(instance=post)
return render(request, 'item/category_edit.html', {'form': form})
So basically after each operation like edit,new I redirect back to from where the call was originally made. (I stoped using it in case of delete since in case of delete the page might not exist any more)
However in one of my questons somebody mentioned that it is better approach to add a path to URL instead of sending URL as parameter to add it in the link
Delete
From samples and documentation I figured out this approach should automatically work in django if I have in my settings in 'context_processors' included 'django.template.context_processors.request'. (However the samples I saw where dated to previous versions of Django)
But this approach didn't work. So my question is does ?next={{request.path}} is still supported in Django 1.8 and if yes what step I am missing.
Any feedback on what approach is better the redirect or the ?next={{request.path}} is welcome as well.

Struggling to understand a django class based view code

I was reading the docs for the django class based view when I encountered a code that I couldn't really understand. If someone could explain the "get" part it would be really helpful.
here is the view code
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views.generic import View
from .forms import MyForm
class MyFormView(View):
form_class = MyForm
initial = {'key': 'value'}
template_name = 'form_template.html'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
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})
What's the "initial" for ? And how the get function is contributing to this code?
The get function renders the form, i.e. produces HTML of the form. When given a dictionary of initial values, then even on the first visit, the form will already be filled — with the initial values. Otherwise it would be empty.
The class variable initial is merely storing the initial values, so they can be used by the get function. You could put the variable also in the get method or omit it entirely:
def get(self, request, *args, **kwargs):
form = self.form_class(initial={'key': 'value'})
return render(request, self.template_name, {'form': form})
(Would not do it though, as the initial values are then slightly less visible, and they really are important.)
Also, have a look at the documentation regarding bound and unbound forms, to understand the difference between initial and default values.
initial = {'key': 'value'}
is a place to set some default values for the form.
self.form_class(initial=self.initial)
uses those defaults to fill the form when there is a GET request.

Django - being able to access page only after HttpResponseRedirect

I have a class based view in which I process the form and redirect the user on successful submission like so:
views.py
def get(self,request):
form = self.form_class()
return render(request, template_name, { 'form' : form })
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
...
return HttpResponseRedirect(reverse('success'))
return render(request, template_name, { 'form' : form })
urls.py
...
url(r'^submit/success', SubmitView.as_view(), name='success'),
...
It is possible to access url directly by typing success/submit. I don't use any authentication on the site and want the user only be able to access the submit/success page after redirection, so that they are not able to access it directly. How do I do it?
If you are using sessions, you can accomplish it like so:
# in the view where form is submitted
if form.is_valid():
request.session['form-submitted'] = True
return HttpResponseRedirect(reverse('success'))
# in the success view
def get(self, request):
if not request.session.get('form-submitted', False):
# handle case where form was not submitted
else:
# render the template
Instead of redirecting, you could POST to the 'success' page.
Then use if request.method == 'POST':
But beware, this is NOT secure, as headers can be spoofed.
Better to just call the success view from within the POST method, I think.
Have you tried something like this:
if form.is_valid():
...
return HttpResponseRedirect(SubmitView.as_view())
Not sure if this works out of the box, but with a few more tricks you might get what you want.
To add to the answer #miki725 posted I would also make sure you change
request.session['form-submitted'] = False
after you have entered the
if not request.session.get('form-submitted', False):
In order to prevent accessing the page directly or using the back and forward on the browser.

Categories

Resources