I am having problem in django. I have created a form in my app where I can take details of a client. Now I want to create a form which can allow me to edit a form. However I am having some problems when I go to /index/edit_client/1, I get this error.
local variable 'form' referenced before assignment
I do not know what the reason why I have got this error, but from what I have looked at, it does not help matters unless of course there is another way how to create an edit form to edit the clients form. Here are some output that can be helpful too.
# urls.py
urlpatterns = patterns('',
(r'^index/$', login_required(direct_to_template), { 'template': 'index.html' }),
(r'^index/clients/$', client_info),
(r'^index/clients_details/(?P<id>\d+)/$', clients_details),
(r'^index/edit_client/(?P<id>\d+)/$', edit_client),
)
# views.py
#login_required
def edit_client(request, id=1):
clients_list = Client.objects.filter(pk=id)
if request.method == 'POST':
form = ClientForm(request.POST or None)
if form.is_valid():
form.save()
return HttpResponseRedirect('/index/clients/')
else: form = ClientForm()
return render_to_response('edit_client.html', {'form': form}, context_instance=RequestContext(request))
#edit_client.html
{% extends "base.html" %}
{% block content %}
<font face="verdana,news gothic,arial,heltevica,serif">
<h3>Edit Client</h3>
</font>
<form method= "POST" action="">
<font face="verdana,news gothic,arial,heltevica,serif">
<div id="form">
<table>
{{form.as_table}}
</table>
<div align="center" STYLE=" margin-right:190px">
<input type="submit" value="Submit" STYLE="background-color:#E8E8E8; color:#181818 "/>
</div>
</div>
</form>
{% endblock %}
This will always run:
return render_to_response('edit_client.html', {'form': form}
But if request.method is not POST, nothing is assigned to form.
Fixed code:
#login_required
def edit_client(request, id=1):
clients_list = Client.objects.filter(pk=id)
form = ClientForm()
if request.method == 'POST':
form = ClientForm(request.POST or None)
if form.is_valid():
form.save()
return HttpResponseRedirect('/index/clients/')
return render_to_response('edit_client.html', {'form': form}, context_instance=RequestContext(request))
In your edit_client method, you pass form in the response, however, if the method wasn't a POST, you won't have initialized a form.
Related
views.py
#login_required(login_url='/login')
def updatepost(request,pk):
post = Post.objects.get(id=pk)
form = PostForm(instance=post)
if request.method =='POST':
form = PostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
form.save()
return redirect ('mainpage')
context = {'form':form}
return render( request, 'postform.html', context )
postform.html
{% include 'main.html'%}
{% block content %}
{%if user.is_authenticated%}
{% if user.id == post.user.id%}
<div>
<form method="POST" action ="">
{% csrf_token %}
{{form.as_p}}
<input type="Submit" value ="Submit"/>
</form>
</div>
{%endif%}
{%endif%}
{% endblock content %}
I am trying to restrict user who is logged in - from updating , deleting other users posts.
However when I Try to use {% if user.id == post.user.id%} , the page becomes blank even for the user who is editing his own post. The same goes for deleting.
It works on mainpages - where posts are displayed (it hides edit and delete buttons).
What is the reason that the post is not showing inside the template ?
I don't understand that even {{post.user}} in postform.html does not appear , neither on deleteform etc. - why this data of objects of a post is not being sent to postform.html ?
You should change the context to access the post instance from your template.
context = {'form': form, 'post': post}
You can only access the context values from the templates. So pass the ones you want to use while you are returning a response.
After a user has submitted a form with data that fails validation, form.errors in fact collects those errors as I've been debugging the issue. However when I render the page after a POST request, the errors will not be parsed in HTML alongside the fields where errors occur.
In other words <ul class="errorlist"> won't render in the html.
What needs to change in order for the validation errors to render in the template when user data doesn't pass validation?
# view that renders the template
#login_required(login_url="/accounts/sign_in/")
def new_profile(request, username):
form = ProfileForm()
import pdb; pdb.set_trace()
if request.method == 'POST':
user_profile = ProfileForm(request.POST)
if user_profile.is_valid():
user_profile.cleaned_data.update(user=request.user)
Profile.objects.create(**user_profile.cleaned_data)
return HttpResponseRedirect(
reverse("accounts:profile", kwargs={'username': username})
)
return render(request, 'accounts/create_profile.html', {'form': form})
# create_profile.html
{% extends 'layout.html' %}
{% block body %}
<form action="{% url 'accounts:new_profile' username=user %}" method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Submit</button>
</form>
{% endblock %}
-> if request.method == 'POST':
(Pdb) n
-> user_profile = ProfileForm(request.POST)
(Pdb) n
-> if user_profile.is_valid():
(Pdb) p user_profile.errors
{'birth': ['Enter a valid date.'], 'bio': ['Add more detail to your bio.']}
(Pdb) p user_profile.as_p()
# As shown when calling .as_p() in the console
<ul class="errorlist">
<li>Enter a valid date.</li>
</ul>
<p>
<label for="id_birth">Birth:</label>
<input id="id_birth" name="birth" placeholder="None" type="text" value="12-23" />
</p>
<ul class="errorlist">
<li>Add more detail to your bio.</li>
</ul>
<p><label for="id_bio">Bio:</label>
<input id="id_bio" name="bio" placeholder="None" type="text" value="Comments" />
</p>
<p><label for="id_avatar">Avatar:</label> <input id="id_avatar" name="avatar" placeholder="None" type="file" /></p>
Supply the form back to the user if it is not valid..
else:
form = ProfileForm()
#login_required(login_url="/accounts/sign_in/")
def new_profile(request, username):
import pdb; pdb.set_trace()
if request.method == 'POST':
form = ProfileForm(request.POST)
if form.is_valid():
form.cleaned_data.update(user=request.user)
Profile.objects.create(**form.cleaned_data)
return HttpResponseRedirect(
reverse("accounts:profile", kwargs={'username': username})
)
else:
form = ProfileForm()
return render(request, 'accounts/create_profile.html', {'form': form})
So I'm working on a Django project and this is my views.py file:
def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
user = User.objects.first() # TODO: get the currently logged in user
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save()
return redirect('board_topics', pk=board.pk)
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'form': form})
When I ran my server, I got an error saying:
UnboundLocalError: local variable 'form' referenced before assignment
This is my new_topic.html file
{% extends 'base.html' %}
{% block title %}Start a New Topic{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item">Boards</li>
<li class="breadcrumb-item">{{ board.name }}</li>
<li class="breadcrumb-item active">New topic</li>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-success">Post</button>
</form>
{% endblock %}
The line if form.is_valid(): fails on a GET request, because you're only defining form when request.method == 'POST'.
This can be fixed by changing some indentation:
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save()
return redirect('board_topics', pk=board.pk)
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'form': form})
I don't find any issue with indentation, but with initialization. Kindly initialize for before rendering it! Make changes as shown below will definitely work out and it's a very genuine and proper way to get rid of it:
def new_topic(request, pk):
board = get_object_or_404(Board, pk=pk)
user = User.objects.first() # TODO: get the currently logged in user
form = NewTopicForm()
if request.method == 'POST':
form = NewTopicForm(request.POST)
if form.is_valid():
topic = form.save()
return redirect('board_topics', pk=board.pk)
else:
form = NewTopicForm()
return render(request, 'new_topic.html', {'form': form})
I'm submitting a form with invalid data: string that contain's less then 5 characters (see forms.py), I see that the form is bound, I print form.errors from the view to see the actual errors, I pass form to the template but form.errors is empty in template!
views.py
def index(request):
if request.method == 'POST':
form = OptionForm(request.POST)
if form.is_valid():
print "VALID"
return HttpResponseRedirect(reverse('main:success'))
elif request.method == 'GET':
form = OptionForm()
print form.is_bound # --> True
print form.errors # --> errors, see below
return render(request, 'main/index.html', {'form': OptionForm})
printed form.errors contains:
<ul class="errorlist"><li>opt<ul class="errorlist"><li>Ensure this value has at least 5 characters (it has 3).</li></ul></li></ul>
forms.py
class OptionForm(forms.Form):
opt = forms.CharField(min_length=5)
index.html
<!-- form errors -->
{{ form.errors }}
<form id="option-set" method="post">
{% csrf_token %}
{{ form.opt }}
<input type="submit" value="submit">
</form>
However, if I pass form.error salong with the form object, I will be able to see them.
return render(request, 'main/index.html', {'form': OptionForm, 'errors': form.errors})
As I know, errors, or other attributes, should be accessible directly in the template.
I can't see where is the bug! :)
You're passing the form class, OptionForm, rather than the instantiated variable form, to the template.
I have a models.py class as below
class Educational_Qualification(models.Model):
user = models.ForeignKey(User)
exam = models.CharField(max_length=40)
pass_month = models.CharField(max_length=40)
I have a views.py as below
def create_qualification(request):
QFormSet = modelformset_factory(Educational_Qualification, extra=3, exclude=("user",))
if request.method == "POST":
formset = QFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
for form in formset.forms:
if form.is_valid():
quali= form.save(commit=False)
quali.user = request.user
quali.save()
return HttpResponse("Saved")
else:
return HttpResponse("Snafu")
else:
formset = QFormSet()
return render_to_response("register/edu.html", {"formset":formset}, context_instance=RequestContext(request))
When I submit the form, it throws up the validation Error. stating that ManagementForm data is missing or has been tampered with'
I have formset.management_form in my template.
What could be the issue?
The error is not in your views or the models, but in the templates.
The right way to render the formset, is:
<form method="post" action="">
<table>
{{ formset }}
</table>
</form>
or
<form method="post" action="">
{{ formset.management_form }}
<table>
{% for form in formset.forms %}
{{ form }}
{% endfor %}
</table>
</form>
I guess, you are looping over the forms in the templates without including the management form?
It also happens if there are multiple views involved and one of them is not aware of the formset prefix.
Get view:
def someview(request):
...
formset = Formset(prefix="foo")
...
Post view (Potentially an Ajax form submit handler):
def ajaxview(request):
...
formset = Formset(request.POST, prefix="foo") # here
...