I can't figure why I'm getting unboundLocalError - python

I am using django 1.8 and python 2.7 but I keep on getting unbounLocalError. It looks all is ok with the code in my views.py. But it keep on local variable 'cd' referenced before assignment. I will be glad if someone could provide the solution
def post_search(request):
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
return render(request, 'blog/post/search.html', {'form': form,
'cd': cd,
'results': results,
'total_results': total_results})
search.html
{% extends "blog/base.html" %}
{% block title %}Search{% endblock %}
{% block content %}
{% if "query" in request.GET %}
<h1>Posts containing "{{ cd.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize }}</h3>
{% for result in results %}
{% with post=result.object %}
<h4> {{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock %}

The problem is that you try to return a context dictionary containing cd, results, and total_results even if your if clauses are not satisified. You should fix your indentation:
def post_search(request):
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
# Indent this line:
return render(request, 'blog/post/search.html', {'form': form,
'cd': cd,
'results': results,
'total_results': total_results})

Related

How can I avoid repetition of code within a function-based view in Django?

I have been researching how can I avoid using snippet of code over and over. The answer probably will involve using (generic) Class-based functions. However, I am a beginner in Django and this seems confusing. Here is my view in views.py:
#login_required(login_url='/login')
def view_list(request, listing_id):
bid = Bid.objects.all().filter(listing=listing_id).order_by('-id')
b_u = bid[0].user
listing = Listing.objects.get(pk=listing_id)
if request.method == "GET":
return render(request, "auctions/view_list.html", {
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
else:
form = BidForm(request.POST)
if form.is_valid():
value = form.cleaned_data
if value['bid'] <= bid[0].bid:
error_check = True
return render(request, "auctions/view_list.html", {
"error_check": error_check,
"alert": f"Your bid is lower than the current bid $({bid[0].bid})! Try placing a higher one.",
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
else:
error_check = False
new_bid = form.save(commit=False)
new_bid.user_id = request.user.id
new_bid.listing_id = listing.id
new_bid.save()
return render(request, "auctions/view_list.html", {
"error_check": error_check,
"alert": "Your bid was successfully placed!",
"form": BidForm(),
"total_bids": bid.count(),
"bid": None if bid == 0 else bid[0].bid,
"listing": listing,
"bid_user": "Your bid is the current bid." if request.user == b_u else None
})
And here is my template code:
{% extends "auctions/layout.html" %}
{% load humanize %}
{% load crispy_forms_tags %}
{% block body %}
{% if error_check == True %}
<div class="alert alert-warning" role="alert">
{{ alert }}
</div>
{% elif error_check == False %}
<div class="alert alert-success" role="alert">
{{ alert }}
</div>
{% endif %}
<div>
<h3>Listing: {{ listing.title }}</h3>
<img src="{{ listing.image }}" alt="Listings' Images">
<p>{{ listing.description }}</p>
{% if not bid %}
<strong>${{ listing.price|stringformat:"1.2f" }}</strong>
{% else %}
<strong>${{ bid|stringformat:"1.2f" }}</strong>
{% endif %}
<p> {{ total_bids }} bid(s) so far. {% if bid_user %} {{ bid_user }} {% endif %}</p>
<form method="POST" name="bidding" action="{% url 'view_list' listing.id %}">
{% csrf_token %}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">$</span>
</div>
<div style="margin: 0; padding: 0 2px; height: 6px;">
{% crispy form %}
</div>
<div class="input-group-append" >
<span class="input-group-text">.00</span>
</div>
<input type="submit" class="btn btn-primary" value="Place Bid">
</div>
</form>
<h4>Details</h4>
<li>Listed by: {{ listing.user }} </li>
<li>Category: {{ listing.category }} </li>
<li>Listing created at: {{ listing.created_at }} </li>
</div>
{% endblock %}
So how can I avoid all this repetition and make the code more succinct. Also, this way when the user places a successful bid, the rendered template does not contain the new information from the form.
The pattern is very simple
def some_view(request):
form = SomeForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
# Form processing
return render(request, "auctions/view_list.html", {
"form": form
})

I have trouble finding the site

I do a search for articles on the site. Filtering should be by article title.
Data from the search should be displayed in the template/ I tried to filter the data using order_by and filter
views.py
def post_search(request):
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
search = request.GET['username']
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Articles).filter(content=cd['query']).load_all()
total_results = results.count()
return render(request,
'news/posts.html',
{'form': form,
'search': search,
'cd': cd,
'results': results,
'total_results': total_results})
posts.html
{% if "query" in request.GET %}
<h1>Posts containing "{{ cd.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize }}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="{% url 'post_search' %}" method="get">
<h3>Search</h3><input style=" border: 3px solid #4242A3; border-radius: 15px ; " type="text" name="search" value=" searchк "> <br>
<input type="submit" value="Search">
</form>
{% endif %}
urls.py
path('search/', views.post_search, name='post_search'),
models.py
class Articles(models.Model):
title = models.CharField(max_length= 200)
post = models.TextField()
date = models.DateTimeField()
img = models.ImageField(upload_to='', default="default_value")
tags = TaggableManager()
article_like = models.IntegerField(default='0')
article_dislike = models.IntegerField(default='0')
view = models.IntegerField(default='0')
def __str__(self):
return self.title
If you are trying to search posts by the title of the articles then you can change your view like this:
def post_search(request):
query = request.GET.get('search')
if query:
results = Article.objects.filter(title__icontains=query).order_by('-date')
total_results = results.count()
return render(request,
'news/posts.html',
{
'results': results,
'query':query,
'total_results': total_results})
else:
messages.info(request,'no results found for {}',format(query))
And in your template you can do like this:
<h1>Posts containing "{{ query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize }}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your {{query}}.</p>
{% endfor %}
<p>Search again</p>
{% else %}

Django update model entry using form fails

I want to update a model entry using a form. the problem is that instead of updating the entry it creates a new entry.
def edit(request, c_id):
instance = get_object_or_404(C, id=int(c_id))
if request.POST:
form = CForm(request.POST, instance=instance)
if form.is_valid():
form.save()
return redirect('/a/b', c_id)
else:
form = CForm(instance=instance)
args = {}
args.update(csrf(request))
args['form'] = form
args['c_id'] = c_id
return render_to_response('a/b.html', args)
HTML code:
<form action="/a/edit/{{ c_id }}/" method="post">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }} {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
<input type="submit" value="Submit"/>
</form>
CForm class code
class CForm(forms.ModelForm):
class Meta:
model = C
fields = ['name', 'code']
You're checking the request for a POST method incorrectly. request.POST isn't a boolean, it contains a dictionary of post variables and is always going to have the CSRF token in it so it will always be "truthy". What you need is request.method.
Instead of:
if request.POST:
Replace it with:
if request.method == "POST":

Can't get past Django MultiValueDictKeyError on specific form id?

So, everytime I submit my form, I always get this error
MultiValueDictKeyError at /template/ 'form-21-id'
Here is my views.py:
class SomeTemplate(TemplateView):
template_name = 'template/index.html'
def get_context_data(self, **kwargs):
context = super(AttendanceTemplate, self).get_context_data(**kwargs)
instruction = Instruction(self.request.user.username)
sections_list = self.request.GET.getlist('sections_list')
term = self.request.GET.get('term', instruction.term)
enrollments = Enrollment.objects.using('api').prefetch_related('profile').filter(section_id__in=['111111'], term=term)
attendanceQuery = Enrollment.objects.using('api').prefetch_related('student').filter(section_id__in=['111111'], term=term)
#logger.error(dir(attendanceQuery))
#logger.error(attendanceQuery.values())
for enrollment in attendanceQuery:
#logger.error(enrollment.student.first_name)
attendance, created = Attendance.objects.update_or_create(
section_id=enrollment.section_id,
term=enrollment.term,
first_name=enrollment.student.first_name,
last_name=enrollment.student.last_name,
email_address=enrollment.student.email_address,
meeting_date=timezone.now(),
section_info=3,
)
something = Attendance.objects.filter(section_id__in=['111111'], term=term)
formset = AttendanceFormSet(queryset=something)
combined = zip(enrollments, formset)
context['combined'] = combined
context['formset'] = formset
return context
def post(self, request):
formset = AttendanceFormSet(request.POST)
logger.error(formset.errors)
if formset.is_valid():
formset = formset.save();
return render_to_response("template/index.html", {'formset': formset},context_instance=RequestContext(request))
else:
return HttpResponse(request.POST)
Here is my index.html file:
<form method="POST" action="">
{% csrf_token %}
{{ formset.management_form }}
{% for enrollment, form in combined %}
{{ form.id }}
{% for hidden in combined %}
{{ hidden.hidden_fields }}
{% endfor %}
<div class="wrapper-formset">
<div>
{{ form.first_name.value }}
{{ form.last_name.value }}
{{ form.email_address.value }}
</div>
<div class="clear-all"></div>
</div>
{% endfor %}
<button type="submit" class="save btn btn-default">Save</button>
</form>
I've included the following key form properties in my template as well:
form.id
management form
hidden fields
What am I missing? Doing it wrong? DB issue?

I keep getting the following error "local variable 'total_results' referenced before assignment"

I am new to django and python, I am following a django tutorial and I keep getting the following error
UnboundLocalError at /blog/search/
local variable 'total_results' referenced before assignment
heres my code
def post_search(request):
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post)\
.filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
template = 'blog/post/search.html'
context = {
'form': form,
'cd': cd,
'results': results,
'total_results': total_results
}
return render(request, template, context)
I also tried it like this origanally because that's how the tutorial had it
return render(request, template, {
'form': form,
'cd': cd,
'results': results,
'total_results': total_results
})
but that also didn't work
I understand what the error message is saying but this is how the tutorial has it. What's the proper syntax to make this work. all guidance is welcome
EDIT: here is the template code
{% extends "blog/base.html" %}
{% block title %}Search{% endblock %}
{% block content %}
{% if "query" in request.GET %}
<h1>Posts containing "{{ cd.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize}}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock %}
If there would not be query GET parameter passed, or the form would not pass validation - in this case total_results and results would not be defined. You need to either provide the default values for that case, e.g.:
def post_search(request):
results = []
total_results = 0
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post)\
.filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
template = 'blog/post/search.html'
context = {
'form': form,
'cd': cd,
'results': results,
'total_results': total_results
}
return render(request, template, context)
Or, throw a specific "validation" error in case there is no query parameter or form is not valid.
def post_search(request):
results = [] # or None
total_results = 0 # or None
form = SearchForm(request.GET or None)
if 'query' in request.GET:
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post)\
.filter(content=cd['query']).load_all()
# count total results
total_results = results.count()
template = 'blog/post/search.html'
context = {
'form': form,
'cd': cd,
'results': results,
'total_results': total_results
}
return render(request, template, context)
else:
return render(request, 'blog/post/search.html', {'form': form,})
else:
return render(request, 'blog/post/search.html', {'form': form,})
Python is pointing out that what will happen if the 'if' condition fails, and still you are using the variable 'total_results' in context. Therefore initialize it as 0 or none as you want. Similar with the 'results' variable too.
EDIT1: Since i don't exactly know what you are trying to achieve, my best guess would be to use this code.
EDIT2: Template code changes
{% extends "blog/base.html" %}
{% block title %}Search{% endblock %}
{% block content %}
{% if results %}
<h1>Posts containing "{{ cd.query }}"</h1>
<h3>Found {{ total_results }} result{{ total_results|pluralize}}</h3>
{% for result in results %}
{% with post=result.object %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% endwith %}
{% empty %}
<p>There are no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="get">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock %}
I've gone through the same book, and noticed the same problem with that particular view. Here was my solution in the view. I left the template the way it was (in the book):
def post_search(request):
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
total_results = results.count()
context = {'form':form, 'cd':cd, 'results':results, 'total_results':total_results}
return render (request, 'blog/post/search.html', context)
else:
form = SearchForm()
context = {'form':form}
return render (request, 'blog/post/search.html', context)
had exactly the same issue ... the code below works
def post_search(request):
results = []
total_results = 0
cd = None
form = SearchForm()
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
results = SearchQuerySet().models(Post).filter(content=cd['query']).load_all()
total_results = results.count()
return render(request, 'search/search.html', {'form':form,
'cd':cd,
'results':results,
'total_results':total_results,
})
But out of interest did you also find that the post_detail view produced similar errors ?? solved that one with this version of the code
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
publish__month=month,
publish__day=day)
#list of active comments for this post
comments = post.comments.filter(active=True)
if request.method == 'POST':
# A comment was posted
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
#create comment object but don't save to DB
new_comment = comment_form.save(commit=False)
# assitgn comment to post
new_comment.post = post
# save the comment to the DB
new_comment.save()
else:
comment_form =CommentForm()
post_tags_ids = post.tags.values_list('id', flat=True)
similar_posts = Post.published.filter(tags__in=post_tags_ids).exclude(id=post.id)
similar_posts = similar_posts.annotate(same_tags=Count('tags')).order_by('-same_tags','-publish')[:4]
args = {}
args['post'] = post
args['comment_form']= comment_form
args['comments'] = comments
args['similar_posts'] = similar_posts
return render(request, 'detail.html', args )

Categories