Local Variable referenced before assignment - python

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.

Related

Pass a variable to another function and render an html template at the same time in django

We want to access the same variable in every function inside our views.py. Since it is not constant, we cannot use it as a global variable.
Is it possible to pass a variable to another function while also rendering an HTML template? What are the alternatives if none exist?
This is our login function in views.py
def loginpage(request):
errorMessage = ''
# Applicant Login
if request.method=="POST":
if request.POST.get('username') and request.POST.get('pwd'):
try:
currentUser=Applicant.objects.get(username=request.POST['username'],pwd=request.POST['pwd'])
currentUser=Applicant.objects.get(username=request.POST['username'])
first = currentUser.firstname
middle = currentUser.middleinitial
last = currentUser.lastname
AppDashboard = ApplicantDashboardPageView(currentUser, request)
except Applicant.DoesNotExist as e:
errorMessage = 'Invalid username/password!'
return render(request, 'home.html')
The currentUser variable inside our login function is the variable we want to pass in this function
def ApplicantdashboardPageView(currentUser, request):
appPeriod = ApplicationPeriod.objects.all()
exam = ExaminationSchedule.objects.all()
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
name=userNaCurrent
print('from storeCurrentUser', name)
if request.method == "GET":
try:
posts = Post.objects.all().order_by('-created_on')
form = PostForm()
#applicantID=currentUser.id
#applicantNotification = Applicant.objects.get(id=applicantID)
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
except ObjectDoesNotExist:
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts,})
return render(request, 'applicantdashboard.html', context={'UserName' : name, 'posts':posts, 'appPeriod':appPeriod, 'exam':exam})
I am new to Django so please bear with me if my question seem too basic. Thank you
Store raw user password is a very big flaw in security. Please read more about Django Authentication system https://docs.djangoproject.com/en/4.1/topics/auth/
Basically, to store critical confidential information like passwords you need to at least, encrypt it. But for passwords you don't need to see the raw value of it, isn't it? Therefore, you just need to hash it and compare it every time you need to authenticate the user. Read more here Best way to store password in database
Django Auth system will also help to solve the issue by injecting the current user into a "global" request object so that you can access it everywhere.
You can do the same by keeping those 2 methods in a class and accessing variables by creating objects for it.

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')

Django form returns is_valid() = False and no errors

I have simple view in django app, which I want to show only when one of the forms is valid. I have something like:
#login_required
#require_role('admin')
def new_package(request):
invoicing_data_form = InvoicingDataForm(instance=request.user.account.company.invoicingdata)
if invoicing_data_form.is_valid():
# all here
return HttpResponse('Form valid')
else:
logger.info("Form invalid")
return HttpResponse(json.dumps(invoicing_data_form.errors)
I always get log info message that form is invalid, however, I get nothing in
invoicing_data_form.errors
It is very strange, because I am validating this form in other view using user input data and it works just fine. Any idea?
EDIT:
Just for clarification.
I am not requesting any data from user in this form.
I am using this form to validate some model instance (this form is subclassing from ModelForm).
That's because you're not "feeding" your form.
Do this:
invoicing_data_form = InvoicingDataForm(instance=invoice, data=request.POST or None)
You have an unbound form.
https://docs.djangoproject.com/en/1.7/ref/forms/api/#bound-and-unbound-forms
A Form instance is either bound to a set of data, or unbound.
If it’s bound to a set of data, it’s capable of validating that data and rendering the form as HTML with the data displayed in the HTML.
If it’s unbound, it cannot do validation (because there’s no data to validate!), but it can still render the blank form as HTML.
To bind data to a form, pass the data as a dictionary as the first parameter to your Form class constructor:
invoicing_data_form = InvoicingDataForm(request.POST or None, instance=invoice)
If you're already giving request.POST to your form using request.POST or None, but it's still invalid without errors, check that there isn't any redirect going on. A redirect loses your POST data and your form will be invalid with no errors because it's unbound.
I got this for AuthenticationForm which needs AuthenticationForm(None, request.POST) see Using AuthenticationForm in Django
I want to expand on the answer by #yuji-tomita-tomita
I typically use a CBV approach in Django, and how I'm handling forms:
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
# do things
Reading the source code I noticed that self.get_form() using get_form_kwargs(self) to populate the form with request.POST, thus getting bound to data. So if you're overloading it like I did:
def get_form_kwargs(self):
company = self.get_company()
return {"company": company}
Make sure to call the super(), and it will finally work:
def get_form_kwargs(self):
company = self.get_company()
kwargs = super().get_form_kwargs()
kwargs.update({"company": company})
return kwargs

Why Django test with blank data post does not work?

I'd like to understand why this testing case does not work: I'm testing that my signup form in my view returns errors when I try to submit an empty form.
In tests.py:
class SignupViewTestCase(TestCase):
def test_signup_post_blank(self):
resp = self.client.post(reverse(signup), {}) # blank data dictionary
self.assertFormError(resp, form='signup_form', field='email',
errors='Ce champ est obligatoire') # French version of "This field is mandatory"
In views.py:
def signup(request):
signup_form = SignupForm(request.POST or None)
if signup_form.is_valid():
ema = signup_form.cleaned_data['email']
raw_pwd = signup_form.cleaned_data['password']
try:
BizProfile.create(ema, raw_pwd)
except IntegrityError:
signup_form.errors['__all__'] = signup_form.error_class([
ERR_USER_EXISTS])
else:
messages.success(request, SUC_ACC_CREA)
messages.info(request, INF_CONN)
return redirect(signin)
return render(request, 'sign_up.html', locals())
When testing manually in my browser, I can see there IS actually an error on the email field when I submit it without data.
But test result says:
AssertionError: The field 'email' on form 'signup_form' in context 0 contains no errors
Any idea of what is happening? Thanks.
Actually, the problem is related to the or None.
That's because an empty dictionary is false. In an "or" condition, Python always returns the second value if the first is false. That means your form is being instantiated just with "None", rather than an empty dictionary: which means it is not being bound at all. A non-bound form does not have any errors.
Changing your test is not a good solution, because a browser would never submit the "email" key without a value: fields without values are simply not send in the POST data, which is why an empty dictionary is the right way to test this. Instead of changing your test, you should use the canonical view pattern, and remove that broken shortcut.
if request.method == 'POST':
signup_form = SignupForm(request.POST)
if signup_form.is_valid():
...
else:
signup_form = SignupForm()
return...

Django Manual Debugging Process Flow

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.

Categories