Django Manual Debugging Process Flow - python

I am in the processing of developing a Django application. I had spent around 40-50 hours researching Django, and I am well on my way to making an application!
However, I am starting to come across "more serious" errors, as some might call them, as I am not able to figure out from my stack trace exactly what the real problem is.
Basically, I click a link on my page, and this error pops up:
Request Method: GET
Request URL: /accounts/profile/
Django Version: 1.5.1
Exception Type: ValueError
Exception Value:
*The view userprofile.views.user_profile didn't return an HttpResponse object.*
That leads me to believe the error is in my views file, except that I was following a tutorial line for line, and I am led to believe the error might be in how forms.py is being used to create the HttpResponse object.
The code in short, is,
form = UserProfileForm(request.POST, instance=request.user.profile)
...
args = {}
args.update(csrf(request)) // security token
args['form'] = form
return render_to_response('profile.html', args)
profile.html is also definitely fine, I tested that, and I am basically calling this from a loggedin.html page where I display a valid user login.
Thanks so much for your help SO, I don't usually ask questions, but I have been stuck on this problem alone for 5-6 development hours. Try not to mock me for not understanding this likely-simply but hidden-to-beginner error :)
Also, I would prefer in the response if you could indicate how you went about solving this error, especially indicating how my thinking is and where the root misunderstanding is.
And in your answers, only reference specific instances of the docs, because I've done plenty of searching, but perhaps it isn't narrowed down quite to what my issue is :D
Thanks again,
James
Comment One: Tutorial
Here is the tutorial I am referring to. I am getting stuck in identifying the bug, as I have all of the code and everything worked until I tried clicking the hyperlink. I am not experienced to where the error is coming from.
Second Comment: Relevant Code
userprofile/views.py
def user_profile(request):
if request.method=='POST':
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/loggedin')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request)) // security token
args['form'] = form
return render_to_response('profile.html', args)
myapp urls.py , userprofile urls.py
(r'^accounts/', include ('userprofile.urls')),
...
url(r'^profile/$', 'userprofile.views.user_profile'),

If that's really your view code, it's a simple indentation error. It should be:
def user_profile(request):
if request.method=='POST':
form = UserProfileForm(request.POST, instance=request.user.profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/loggedin')
else:
user = request.user
profile = user.profile
form = UserProfileForm(instance=profile)
args = {}
args.update(csrf(request)) // security token
args['form'] = form
return render_to_response('profile.html', args)
What's going on is that when request.METHOD is GET, there's no else clause on the initial if, so the view function ends without returning anything. Instead, you want to create the form, add it to the context, and render it - either because it's a GET request or because there were errors in the form, and you want to rerender the context to show those errors and allow the user to correct them.

Related

How to remove built-in errorlist of Django model forms?

I made a model form in Django that enables user to create an instance of that model. All is well save one thing. Built-in error lists of Django is annoying. I want those errors to show itself when user actually made that error. When I open my page it looks like this
Please help me how do I remove these errorlist temporarily so that when user left input fields empty. It pop ups itself?
I don't know if you need these codes to solve this problem, but here it is:
views.py file here
#login_required(login_url='/accounts/login/')
def article_create(request):
article_form = ArticleForm(request.POST or None, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return render(request, 'blog/article-create.html', {'form': article_form})
Thank you
You use the request.POST or None idiom - which I dislike, but never mind - for the data parameter, but not for the files parameter. Since you pass an (empty) dict for the files, Django takes your form as bound, and therefore tries to validate it.
If you insist on using this idiom, you need to do it for both:
article_form = ArticleForm(request.POST or None, request.FILES or None)
But really, it's much more Pythonic to be explicit:
if request.method == 'POST':
article_form = ArticleForm(request.POST, request.FILES)
if article_form.is_valid():
instance = article_form.save(commit=False)
instance.author = request.user
article_form.save()
return redirect('/')
else:
article_form = ArticleForm()
return render(request, 'blog/article-create.html', {'form': article_form})
Note, you always need to redirect after a successful POST, as I have done above.

Django file field update causing error even though not required

I have an app that serves to update certain fields of a model. There are 4 possible fields that could be updated: resolution, upload4, upload5, and upload6. The upload fields are NOT required. If I do not include the request.FILES line, the uploaded file will not be saved to the database, but it seems like because I've included it, I need to always upload the 3 files, even though they are not required. The exception I am getting is "MultiValueDictKeyError" on the POST. How can I fix this? I want the option to add 3 files, but I don't want to have to every time. I understand how to make a field not required, I don't know how to code the request.FILES to understand that it is not required.
views.py
#login_required(login_url='/login/')
def report(request, case_id):
form = ReportForm()
case = get_object_or_404(Incident, pk=case_id)
# if this is a POST request we need to process the form data
if request.POST:
# create a form instance and populate it with the data from the request:
form = ReportForm(request.POST)
if form.is_valid():
resolution = (form.cleaned_data['resolution']) # grabbing action_taken from user input
case.resolution = resolution
case.upload4 = request.FILES['upload4']
case.upload5 = request.FILES['upload5']
case.upload6 = request.FILES['upload6']
case.status = Status.objects.get(status='closed')
case.save(update_fields=['resolution', 'status', 'upload4', 'upload5', 'upload6'])
context = { 'case': case,
'form': form}
return HttpResponseRedirect(reverse('dashboard_app:dashboard'))
template = "report.html"
#form = CaseForm()
context = { 'case': case,
'form': form}
return render(request, template, context)
The point is that you are ignoring the validation that form does, and going straight back to the data from the request. So, yes, that will break if the forms are not there. But this is exactly why we use forms.
case.upload4 = form.cleaned_data['upload4']
etc.
It would be even easier if you used a ModelForm; then you could pass case as the instance argument of the form, and just do form.save(), replacing almost all the code inside your is_valid block.
This will solve the problem.
case.upload4 = request.FILES.get('upload4')
case.upload5 = request.FILES.get('upload5')
case.upload6 = request.FILES.get('upload6')

How to cancel navigation in a django view when an error occurs

I would like to do the following:
I am displaying view1, with template1. There is a link to view2, rendering template2
The user clicks the link
When processing view2, an error occurs
Now come the interesting part: I want to cancel rendering of view2, activate an error message, and display the previous view1/template1 (a kind of redirection, but not quite) But in view2 I do not know that I was coming from view1, and I also probably do not have the context that I had when view1 was rendered
How can I re-render view1, with the error message genarated in view2, so that the user the only new thing that sees is that error message? The full template1 should be rendered as before, including any form values that the user has entered.
This is similar to form validation, but would happen in the destination view instead of the form view.
Is this at all doable, or am I trying to do something completely impossible?
There doesn't seem to be a way to "abort" a view, but you can get the behavior you want with the following pattern:
from django.contrib import messages
from django.shortcuts import redirect
def view1(request):
if request.method == 'GET':
initial_data = request.session.get('post_data', {})
form = MyForm(initial=initial_data)
...
def view2(request):
...
if error:
request.session['post_data'] = request.POST
messages.add_message(request, messages.ERROR, "Oops!")
return redirect(request.META.get('HTTP_REFERER', '/'))
See this stack post for why preserving the POST data is painful and alternative ways of doing so.

How to deal with errors in large formsets?

I'm just starting with formsets and I have trouble making a user friendly error system.
So the user sees a list view that shows him all instances of a model already written into a formset. This pages is meant to show him the data and edit it as well.
Before I was using a lot of individual forms with a save button for every form. But now I want to improve it to have a formset that provides a single save button for all of the forms.
But there comes my problem: Before I used to send the user via "action" to another url (e.g. .../update/ ) which processes the request and then sends him back to the list view he saw before. That's to avoid multiple submits when hitting F5.
But now if I do this and only a single form is wrong all the information the user had entered is lost.
So instead I stopped using the extra URL and made the processing part of the list view. Now I can use form.error on every form, but also the user resubmits when hitting F5.
Is there a way to have both?
Also: I have 1 extra form. But if the user changes it, and I feed the POST data into the formset, save it and then put it back to the page I lost the extra formset, because now the former extra is showing the newly entered instance and there is no true extra field left until the page is refreshed without sending post data.
Here is my View:
class BVServerListView(View):
def get(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(queryset=eigene_server)
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
def post(self, request):
eigene_server = Server.objects.filter(user=request.user)
EigeneServerFormset = modelformset_factory(Server, extra=1, form=ServerForm)
eigene_server_formset = EigeneServerFormset(request.POST, request.FILES)
for form in eigene_server_formset.forms:
if form.data.get('delete', False):
server = Server.objects.get(user=request.user, name=form.data['name'])
server.delete()
else:
if form.has_changed() and form.is_valid():
server = form.save(commit=False)
server.user = request.user
server.save()
context = {'eigene_server': eigene_server_formset,}
return render(request, 'bildverteiler/server_list.html', context)
There is no difference between using a single form or a formset here. The answer is the same: post to the same view, but redirect after a successful save.
The other thing that you are doing wrong here is to validate and save individual forms one by one. Don't do that, because you could end up with a situation that the first forms are valid and get saved, but subsequent ones are invalid and therefore the formset needs to be redisplayed. Instead, validate the formset as a whole:
if eigene_server_formset.is_valid():
for form in eigene_server_formset.forms:
if form.cleaned_data.get('delete'):
... delete ...
else:
form.save()
return HttpResponseRedirect('somewhere_else')
return render...

Local Variable referenced before assignment

I was wondering if you guys could help. I'm trying to do a simple view where it sends the user to the client creation form, but I keep getting this error:
local variable 'form' referenced before assignment
Looking at my code, I can't see whats wrong.
def add_client(request):
user = request.user
if request.method =='POST':
form = AddClientForm(request.POST)
if form.is_valid():
client = form.save(commit=False)
client.save()
return HttpResponseRedirect('/')
else:
form = AddClientForm()
return render_to_response('clients/addClient.html', { 'form': form, 'user': user, }, context_instance=RequestContext(request))
Anyone tell me where I went wrong?
This is what is happening:
The if block is not being entered.
The form variable is not defined.
You then attempt to refer to the form variable in the return statement.
As to how to fix it, that's really for you to decide. What the fix is depends on what you want your code to do in case the request method is not POST.
You almost certainly want to de-indent this part:
else:
form = AddClientForm()
That is, on the initial GET of the page, use a blank client form, then when the page is POSTed, use the request POST data to fill in the form object.

Categories