Django 1.6 - reusable view with redirect - django

I have just made reusable comments app that can be attached to any model, please see view I have problem with ( I didn't include whole logic, only the one relevant to the question).
When there is no request.POST, all is working fine, however when I try to add the comment, after it is saved there is some problem with redirect, I am getting error
dictionary update sequence element #0 has length 0; 2 is required
problematic line is context.update(dictionary). It looks like it is empty when comment is added, but I don't understand why.
My logic is this:
when there is no request.POST, view add_comment will return
{'comment_form': comment_form, 'comments': comments}
when request.method==POST' , context.update(dictionary) shouldn't
be even executed, because of return redirect(node). It should result
in starting code executing in view profile, because that's where
redirect(node) should lead to.
I know I could just use redirect in profile.views.py, but then I will need to do this for every view with added comments, which is extremely unconvenient.
comment.views.py
from django.shortcuts import redirect
from comment.forms import AddCommentForm
from comment.models import Comment
def add_comment(request, node):
if request.user.is_authenticated():
user = request.user
else:
user = None
comment_form = None
comments = Comment.objects.get_comments(node) # custom manager method for getting all comments
if user:
if request.method == 'POST':
comment_form = AddCommentForm(request.POST)
if comment_form.is_valid():
comment_form.save(node=node, user=user) # custom form save method, updating missing fields
return redirect(node) #redirect to node.get_absolute_url()
else:
comment_form = AddCommentForm()
return {'comment_form': comment_form, 'comments': comments}
profile.views.py - another app, I want to reduce the code for adding comment by only referring to view add_comment
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.models import User
from comment.views import add_comment
def profile(request, id):
user = get_object_or_404(User, id=id)
dictionary = add_comment(request, user)
context = {'user': user}
context.update(dictionary) #problematic line
return render(request, 'profile/profile account.html', context)

The problem is that add_comment can return something that isn't a dictionary: that is, a redirect (which is a subclass of HttpResponse). You could always check the type of what is returned before using it:
result = add_comment(request, user)
if not isinstance(result, dict):
return result
else:
context.update(result)

Related

The view formApp.views.formRegister didn't return an HttpResponse object

Views.py
from django.shortcuts import render
from . models import Registerform
# Create your views here.
def formRegister(request):
if request.method == 'POST':
if request.POST.get('firstN') and request.POST.get('lastN') and request.POST.get('Email') and request.POST.get('pass'):
Registerform = Post()
Registerform.firstName = request.POST.get('firstN')
Registerform.lastName = request.POST.get('lastN')
Registerform.email = request.POST.get('Email')
Registerform.password = request.POST.get('pass')
Registerform.save()
return render(request, 'formApp/formreg.html', context_instance=RequestContext(request))
As you can see in the above error image, my code is not functioning properly. I have added my models.py and views.py code. Please help me to resolve the issue.
Why it doesn't work
Every django view, has to return a valid HttpResponse object (as the error message says). That is this part of your code:
return render(request, 'formApp/formreg.html', context_instance=RequestContext(request))
The render function is a shortcut which will return such an object.
The problem though is that, all of this is wrapped inside an if request.method == 'POST':, and so if the request is something that is not a POST, the function just returns nothing. Hence the error
How to fix it
As you can see in the screen shot you've provided, the request method is a GET, so you need to add some code to deal with the case of a GET request.
Add something like this:
if request.method == "GET":
return render(...) # put what you want here

Redirect user to Django 500 error page from function instead of view?

I have a Django view that looks for a variable in the user's session and, if it can't find it, raises a 500 error and redirects the user to my custom 'project/templates/500.html' page.
# views.py
def process_country(request, template):
try:
country = request.session['country']
except KeyError as e:
msg = "Key %s not found for uid %s" % ('country', request.user.id)
log.error(msg, exc_info=True)
return render(request, '500.html', status=500)
if request.method == "POST":
# do stuff...
pass
else:
form = MyForm(initial={'country': country})
context = {'form': form}
return render(request, template, context)
This view works as intended if the 'country' session variable doesn't exist. However, what I'd like to do is move this ugly block of exception handling code to a helper function:
# views.py
from utils import get_from_session
def process_country(request, template):
country = get_from_session(request, 'country') # Helper
if request.method == "POST":
# do stuff...
pass
else:
form = MyForm(initial={'country': country})
context = {'form': form}
return render(request, template, context)
# utils.py
from django.shortcuts import render
def get_from_session(request, key):
try:
value = request.session[key]
return value
except KeyError as e:
msg = "Key %s not found for uid %s" % (key, request.user.id)
log.error(msg, exc_info=True)
# Neither of these work!
#return render(request, '500.html', status=500)
#render(request, '500.html')
The problem is that in this second version, the user doesn't get redirected to the custom 500.html error page. Instead, Django displays the template passed to the process_country view but it embeds the raw HTML contained in the 500.html page in that template. I guess I could do an HttpResponseRedirect(reverse('500-page')) but that would entail creating a view and it doesn't feel like the right solution. What's going on here? Can I redirect the user to the custom 500.html template from a function and if so, how?
Thanks.
Raise an error this display 500.html in production automaticaly
raise KeyError
But this wrong way, you should never raise exceptions for user. Instead this you may checks country in your view and if it's not exists, for example, display some message for user in template.

Handling form from different view and passing form validation through session in django

I have a requirement here to build a comment-like app in my django project, the app has a view to receive a submitted form process it and return the errors to where ever it came from. I finally managed to get it to work, but I have doubt for the way am using it might be wrong since am passing the entire validated form in the session.
below is the code
comment/templatetags/comment.py
#register.inclusion_tag('comment/form.html', takes_context=True)
def comment_form(context, model, object_id, next):
"""
comment_form()
is responsible for rendering the comment form
"""
# clear sessions from variable incase it was found
content_type = ContentType.objects.get_for_model(model)
try:
request = context['request']
if request.session.get('comment_form', False):
form = CommentForm(request.session['comment_form'])
form.fields['content_type'].initial = 15
form.fields['object_id'].initial = 2
form.fields['next'].initial = next
else:
form = CommentForm(initial={
'content_type' : content_type.id,
'object_id' : object_id,
'next' : next
})
except Exception as e:
logging.error(str(e))
form = None
return {
'form' : form
}
comment/view.py
def save_comment(request):
"""
save_comment:
"""
if request.method == 'POST':
# clear sessions from variable incase it was found
if request.session.get('comment_form', False):
del request.session['comment_form']
form = CommentForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
if request.user.is_authenticated():
obj.created_by = request.user
obj.save()
messages.info(request, _('Your comment has been posted.'))
return redirect(form.data.get('next'))
else:
request.session['comment_form'] = request.POST
return redirect(form.data.get('next'))
else:
raise Http404
the usage is by loading the template tag and firing
{% comment_form article article.id article.get_absolute_url %}
my doubt is if am doing the correct approach or not by passing the validated form to the session. Would that be a problem? security risk? performance issues?
Please advise
Update
In response to Pol question. The reason why I went with this approach is because comment form is handled in a separate app. In my scenario, I render objects such as article and all I do is invoke the templatetag to render the form. What would be an alternative approach for my case?
You also shared with me the django comment app, which am aware of but the client am working with requires a lot of complex work to be done in the comment app thats why am working on a new one.
I dont see the problem with security, except situation when you using cookies for stroring session. The performance depends on what kind of session backand you are using as well. But I cant find the point why are you complicating things!
And I dont thing that touching session in template tag is a good idea at all.
And maybe Take a look at django Comments Framework
Update:
Ok. I cant see the problems in this approach except complication. For example in my project, i'm using ajax to send data and validate it right in the comments view, therefore I do not require to redirect to original page. Other thing is that I pass the initialized Form in article view, so i'm not using templatetags.
Can provide you with my approche for example purposes:
from forms import CommentForm
from models import Comment
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import simplejson
from etv_persons.person.models import Person
from django.contrib import messages
def create_comment(request,slug):
if request.method != 'POST' or not request.POST or request.user.is_anonymous():
return HttpResponseForbidden('Доступ запрещен')
person = get_object_or_404(Person,slug=slug)
form = CommentForm(data=request.POST)
if form.is_valid():
Comment.objects.create(user_id=request.user.id, person=person,text=form.cleaned_data['text'])
if request.is_ajax():
msg={'msg': 'Cement was send',}
else:
messages.info(request, 'COmment was send.')
else:
if request.is_ajax(): msg={'msg': 'Error.',}
else: messages.info(request, 'Error.')
if request.is_ajax():
return HttpResponse(simplejson.dumps(msg),content_type='application/json')
else:
return redirect('person_details',**{"slug":slug,"ptype":person.type})
And in the article view we just do:
response['comment_form'] = CommentForm()
And yes, I do not validate the comments form. There is no reason. Just one text input.

Redirect / return to same (previous) page in Django?

What are the options when you want to return the user to the same page in Django and what are the pros/cons of each?
Methods I know:
HTTP_REFERER
GET parameter containing the previous URL
Session data to store the previous URL
Are there any other?
One of the way is using HTTP_REFERER header like as below:
from django.http import HttpResponseRedirect
def someview(request):
...
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
Not sure of cons of this!
100% working Example
For Class Based View and Function:
from django.http import HttpResponseRedirect
...
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
or
from django.http import HttpResponseRedirect
...
return HttpResponseRedirect(self.request.META.get('HTTP_REFERER'))
Example -
class TaskNotificationReadAllView(generic.View):
def get(self, request, *args, **kwargs):
TaskNotification.objects.filter(assigned_to=request.user).update(read=True)
print(request.META.get('HTTP_REFERER'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
While the question and answer is old, I think it's lacking a few options. I have not find any cons with the methods, I would be happy to know if there are any?
request.path_info
request.get_full_path()
request.build_absolute_uri()
from django.shortcuts import redirect
redirect(request.path_info) # No query parameters
redirect(request.build_absolute_uri()) # Keeps query parameters
redirect(request.get_full_path()) # Keeps query parameters
In django view suppose you are not logged in but click on some content that content trigger some url like /board/2/new_topic then #login_required will redirect you to login page with this url
http://localhost:8000/signin/?next=/boards/2/new_topic/
so our aim is redirect to http://localhost:8000/boards/2/new_topic/ page after successful login so one line we will have to add
if 'next' in request.GET:
return redirect(request.GET['next'])
then if it next is there then it will redirect according to that other normal redirect .
Views.py :
def signin(request):
if request.method == "POST":
user_login_form = UserLoginForm(request.POST)
email = request.POST['email']
password = request.POST['password']
user = authenticate(request, email=email, password=password)
if user and user.is_active:
login(request, user)
if 'next' in request.GET:
return redirect(request.GET['next'])
else:
return redirect('home')
else:
return render(request, 'signin.html', context={'form': user_login_form})
else:
user_login_form = UserLoginForm()
return render(request, 'signin.html', context={'form': user_login_form})

Django View Value Error

I'm hitting a wall with this error. I'm sure I'm overlooking something basic, just can't seem to figure it out...
ValueError at /sing/register
The view sing.singer.views.grade didn't return an HttpResponse object.
the view file...
from django.shortcuts import render_to_response
from django import forms
from django.http import HttpResponseRedirect
from django.template import Template, RequestContext
from dash.forms import GradeForm
def register(request):
if request.method == 'POST':
form = GradeForm(data=request.POST)
if form.is_valid():
new_dash_profile = form.save()
new_user = form.save()
return HttpResponseRedirect("/success/")
else:
form = RegisterForm()
return render_to_response('grade.html',{'form':form},context_instance=RequestContext(request) )
my urls.py
urlpatterns = patterns('dashboard.dash.views',
(r'^sing/register','register' ),)
my settings.py
TEMPLATE_DIRS = (
"/home/django/testing/sing/grade/templates",)
def register(request):
if request.method == 'POST':
form = GradeForm(data=request.POST)
if form.is_valid():
new_dash_profile = form.save()
new_user = form.save()
return HttpResponseRedirect("/success/")
else:
form = RegisterForm()
return render_to_response('grade.html',{'form':form},context_instance=RequestContext(request) )
your indents look off?
Initially you are entering the view with request != 'POST' which will never reach that else statement at the bottom, so you dont get a HttpResponse.
The other thing that looks strange is even if you fix your indents, you show the RegisterForm initially, and after the post request, you put the data from your RegisterForm into a GradeForm, if that doesn't validate you show pass your GradeForm to your template. Is this what you intended?
also in your urls.py I would add / to:
(r'^sing/register','register' ),)
like:
(r'^sing/register/','register' ),)
unless you want it to match (for example):
www.site.com/sing/registerasdf/
i might even suggest using '/$' at the end like this:
(r'^sing/register/$','register' ),)
to prevent matches to (for example):
www.site.com/sing/register/asdf/asdf/asdf/
www.site.com/sing/register/asdf/asdf/
www.site.com/sing/register/asdf/
Judging from the code, the only time it does not return a HttpResponse is when it's not a POST request. Maybe you are doing a GET instead?
I think its your HttpResonseRedirect. I can't say I've used it that often (if at all). If I were you I would try shortcut redirect
http://docs.djangoproject.com/en/dev/topics/http/shortcuts/#redirect