reason behind "local variable 'variable ' referenced before assignmen" ERROR - python

I am not much familiar with django. Was working on a learning project. So here I am getting the error which says UnboundLocalError, "local variable 'url_array' referenced before assignment" . Here's my form.py , views.py and html code. Please have a look and give me a solution.
forms.py
from django import forms
from .models import Images
class ImageForm(forms.ModelForm):
class Meta:
model = Images
fields = ('__all__')
views.py
class Upload(View):
def post(self, request):
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
imageZip=request.FILES
fs = FileSystemStorage()
url_array = []
with ZipFile(imageZip['image'], 'r') as zip:
zip.extractall()
unzip_file= zip.namelist()
for files in unzip_file:
with open(files, "rb") as file:
name = fs.save('read.jpg', file)
url_array.append(fs.url(name))
else:
form = ImageForm()
return render(request, 'toDo_app.html', context = {'form': form, 'url':url_array})
toDo_app.html
<form class="" enctype="multipart/form-data" action="/upload/" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" >Upload</button>
</form>
<br>
{% if url %}
<p>Uploaded file: </p>
<ul>
{% for urls in url %}
<li> {{ urls }} </li>
{% endfor %}
</ul>
{% endif %}
So my error is at return render(request, 'toDo_app.html', context = {'form': form, 'url':url_array}) line.
Thanks for your time and I would be really grateful for an explanation and solution

The variable url_array is initialised within the one body of the if statement. There is a chance that the if statement is never evaluated as True and hence the url_array will be uninitialised by the time your function returns. The function will not be able to return render(...) because url_array has no value.
Just make sure that the url_array is initialised with some default value outside the if statement, in the same scope with the return statement.

assign a url_array before if block
def post(self, request):
url_array = []
form = ImageForm(request.POST, request.FILES)

Related

Django - problem with writing to database

I have a problem, the urls form works but I can't see the records in url/admin, can I ask for help, thank you :D
SOF wants me to add more details otherwise it doesn't transfer, I don't know what more I can add, generally temapals and urls work.
class Note(models.Model):
"""..."""
notes = models.CharField(max_length=100, unique=True)
description = models.TextField()
class Meta:
verbose_name = "Note"
verbose_name_plural = "Notes"
def __str__(self):
return self.notes
class NoteView(View):
def get(self, request):
if request.method == 'POST':
textN = Note.objects.all().order_by('notes')
form = NoteAddForm(request.POST)
if form.is_valid():
form.save()
return redirect('Files/menu')
else:
textN = NoteAddForm()
return render(request, 'Files/note.html', {'textN': textN})
class NoteAddForm(forms.ModelForm):
"""New note add form"""
class Meta:
model = Note
fields = '__all__'
{% extends 'Files/base.html' %}
{% block title %}Notatnik{% endblock %}
<h2>Notatnik Dietetyka/ Zalecenia ręczne </h2>
{% block content %}
<form action="/send/" method="post">
{% csrf_token %}
{{ textN }}
<label>
<input type="text" class="btn btn-second btn-lg">
<button>Wyślij formularz</button>
</label>
</form>
<button type="button" class="btn btn-primary btn-lg">Powrót</button>
{% endblock %}
Within your NoteView class in views.py file is where the issue is.
I see you have an if statement checking for if request.method == 'POST' within the class-based view get(). The get() is equivalent to if request.method == 'GET'. Therefore, what you might want to do is to override the post() on the class instead. For example:
class NoteView(View):
template_name = 'Files/note.html'
# Use the get method to pass the form to the template
def get(self, request, *arg, **kwargs):
textN = NoteAddForm()
return render(request, self.template_name, {'textN': textN})
# Use the post method to handle the form submission
def post(self, request, *arg, **kwargs):
# textN = Note.objects.all().order_by('notes') -> Not sure why you have this here...
form = NoteAddForm(request.POST)
if form.is_valid():
form.save()
# if the path is... i.e: path('success/', SucessView.as_view(), name='success')
return redirect('success') # Redirect upon submission
else:
print(form.errors) # To see the field(s) preventing the form from being submitted
# Passing back the form to the template in the name 'textN'
return render(request, self.template_name, {'textN': form})
Ideally, that should fix the issue you're having.
Updates
On the form, what I'd suggest having is...
# Assuming that this view handles both the get and post request
<form method="POST"> # Therefore, removing the action attribute from the form
{% csrf_token %}
{{ textN }}
# You need to set the type as "submit", this will create a submit button to submit the form
<input type="submit" class="btn btn-second btn-lg" value="Submit">
</form>

Solving the Error " TypeError at /editprofile/ context must be a dict rather than set."

I got some troubles with solving of "TypeError at /editprofile/
context must be a dict rather than set." in my Django project. I'm trying to allow the users to edit their personal information but I keep getting an error. I tried to read everything here and in other pages but nothing has helped, I would love to know how to solve this if anyone could direct me or give me some tips on how to fix this.
forms.py
class EditProfileForm(UserChangeForm):
class Meta:
model = User
fields = (
'username',
'email',
'country'
)
views.py
def editprofile(request):
if request.method == 'POST':
form = EditProfileForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return render(request, 'movies_app/profile.html')
else:
form = EditProfileForm(instance=request.user)
args = {'form', form}
return render(request, 'movies_app/editprofile.html', args)
editprofile.html
{% extends 'base.html' %}
{% block head %}
<title>Edit form</title>
{% endblock %}
{% block body %}
<form action="." method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% endblock %}
In your views.py args should be a dict, you should replace the , for a :.

Formatting and incrementing a Django form in HTML

OK so I have a running form in Django that updates the model and displays this on the page, but I was hoping to better format it. What happens is that the page displays all data imputed in the form. What I want to do is to numerically list it. This is what I have in my home.html right now:
{% extends 'base.html' %}
{% block body %}
<div class="container">
<h1>Home</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
{% for post in posts %}
<h2>Object:{{ post.post }}</h2>
{% endfor %}
</div>
{% endblock %}
So say I have data "a", "b", and "c". It would display itself as
Object: a
Object: b
Object: c
If I added d to the form, it would add
Object: d
What I'm hoping to do is add an increment to this so it displays itself as
Object 1: a
Object 2: b
Object 3: c
And add d as
Object 4:
How would I go about implementing this? Another thing I wanted to know is whether I can change the "Post:" comment next to the form. Right now my form displays itself with "Post:" at the left side. Is there a way to edit this?
EDIT: Here's my view.py file:
from django.shortcuts import render, redirect
from firstapp.forms import IndexForm
from django.views.generic import TemplateView
from firstapp.models import Post
class HomePage(TemplateView):
template_name = 'home/home.html'
def get(self, request):
form = IndexForm()
posts = Post.objects.all()
args = {'form': form, 'posts': posts}
return render(request, self.template_name, args)
def post(self, request):
form = IndexForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
post.save()
text = form.cleaned_data['post']
form = IndexForm()
return redirect('home:home')
args = {'form': form, 'text': text}
return render(request, self.template_name, args)
What I want to do is to numerically list it
https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#for
You're looking for "forloop.counter":
{% for post in posts %}
<h2>Object {{ forloop.counter }}: {{ post.post }}</h2>
{% endfor %}
Right now my form displays itself with "Post:" at the left side. Is there a way to edit this?
It looks like this is because your model is called "Post".
from firstapp.models import Post
By using the:
{{ form.as_p }}
Django will render a default set of form fields and labels. You can override the way those forms fields are displayed by modifying your IndexForm class, or manually render the entire form yourself in your home.html.
https://docs.djangoproject.com/en/1.11/topics/forms/#form-rendering-options

Django: How to have multiple "add another field" buttons in a form

I'm new to django and I'm having a lot of trouble with forms.
I'm making a calculation-based tool and I need to be able to have an arbitrary number of inputs.
As a really basic example, let's say I want to make a calculator that will sum and subtract any number of inputs. Each number to be added or subtracted is in its own number field. Both the list of "adding" fields and the list of "subtracting" fields has its own "add another field" button.
For starters, here's something that adds two inputs (since I can't figure out how to implement even 1 "add another field button" or understand the answer to it).
views.py
from __future__ import unicode_literals
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from .forms import AddForm
def _from_str(s):
try:
s = int(s)
except ValueError:
try:
s = float(s)
except ValueError:
pass
return s
#csrf_exempt
def web_adder(request):
if request.method == 'POST':
form = AddForm(request.POST)
# form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
if form.is_valid():
return web_adder_out(request, _from_str(form.cleaned_data['addend0']), _from_str(form.cleaned_data['addend1']))
else:
form = AddForm()
# form = MyForm()
return render(request, 'addercontent.html', {'form': form})
def web_adder_out(request, a, b):
return render(request, 'addercontentout.html', {'content':[a + b]})
forms.py
from django import forms
class AddForm(forms.Form):
addend0 = forms.CharField(label='first addend', max_length=100)
addend1 = forms.CharField(label='second addend', max_length=100)
addercontent.html
{% block content %}
<p>This is a web adder</p>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">Enter</button>
</form>
{% endblock %}
addercontentout.html
{% block content %}
{% for c in content%}
Result: {{c}}
<br>
Return
{% endfor %}
{% endblock %}
Don't use Django for the field generation. I would do all of it via HTML. Run your setup that you currently have, and you should be able to look at the page source to see how the inputs are structured. Then you can manually write the form in HTML, with JavaScript adding fields in as needed.
Something like this? (not tested, I haven't implement add button)
forms.py
class CalcForm(forms.Form)
first = forms.IntegerField()
second = forms.IntegerField()
def add(self):
first = self.cleaned_data['first']
second = self.cleaned_data['second']
return first + second
views.py
def index(request):
if request.method == "POST":
form = CalcForm(request.POST)
if form.is_valid():
result = form.add()
return render(request, 'your_result_template.html', {'result': result})
else:
form = CalcForm()
return render(request, 'your_template.html', {'form': form})
your_template.html
{% block content %}
<p>This is a web adder</p>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">Enter</button>
</form>
{% endblock %}
your_result_template.html
{% block content %}
<p>Sum:</p>
<h2>{{ result }}</h2>
{% endblock %}
Edit: For field generation you may need javascript.
I don't know why you want to use django for this kind of app.

Django ~ WSGIRequest with a form added via get_context_data method

I am getting this error when i visit my page:
Caught AttributeError while rendering: 'WSGIRequest' object has no attribute 'get'
The error kicks in on line "17" of my html, which is the line that outputs form.as_p
The html looks like this:
{% extends "base.htm" %}
{% block content %}
{% if story_list %}
{% for story in story_list %}
<div class="Story">
{{ story.title }}
</div>
{% endfor %}
{% else %}
<p>No stories are present - enter one below</p>
{% endif %}
<h3>Create a new story</h3>
<form action="/addStory" method="post">
{% csrf_token %}
{{ form.as_p }} ***THIS IS LINE 17***
<input type="submit" value="Submit"/>
</form>
{% endblock %}
The problem is i have a view that does two things, and from the django tutorials overrode the get_context_data method to add the second item to the django context. Because, um, that's what i'm meant to do, riiiiiiiiight?
#for showing of the stories!
class StoryShowView(ListView):
model = StoryForm
def get_queryset(self):
return getStoryItemsForUser(self.request)
def get_context_data(self, **kwargs):
context = super(StoryShowView,self).get_context_data(**kwargs)
context['form'] = createNewStoryForm(self.request)
return context
Where, well, the method createNewStoryForm just does this:
def createNewStoryForm(request):
return StoryForm(request)
and StoryForm is just this:
class StoryForm(ModelForm):
class Meta:
model = Story
ordering = ['create_date']
and the Story model is a normal model, that probably isn't part of the problem, but, hey, i am a cutting and a pasting, so here goes!
class Story(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
title = models.CharField(max_length=100)
is_closed = models.BooleanField()
is_random = models.BooleanField() # for uncategorised stories. Only one of these.
result = models.CharField(max_length=20) #how did the relo work out?
create_date = models.DateTimeField('date created')
def __unicode__(self):
return self.title
Any ideas what i am doing wrong?
UPDATE:
ah, it was the line::
return StoryForm(request)
I take it i can either pass in a "request.POST" or nothing, is that it?
Probably you're right and you were passing request instead of request.POST, reqest.GET or request.REQUEST to the constructor of your form. See the doc on how to use forms:
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
Two problems that I can see. The easy one being that you can simply replace this line:
context['form'] = createNewStoryForm(self.request)
with
context['form'] = StoryForm(request.POST, request.FILES)
Finally shouldn't this:
class StoryShowView(ListView):
model = StoryForm
Be:
class StoryShowView(ListView):
model = Story

Categories