Django template not iterating over object - python

I am using Django to render a menu of items. I can get the content I want just fine, but when I return it to the django template, it's just a json string so it won't iterate correct. How do I tell django to return it as an iterable object?
I ran across this article. Maybe I want to merge query sets?
Python
def index(request):
if not request.user.is_authenticated:
return redirect('/login', {'message': None})
try:
menu_categories = MenuCategory.objects.all()
menu = []
for cat in menu_categories:
items = MenuCategoryItems.objects.filter(category_id=cat.id).all()
menu.append({'category': cat, 'items': items})
context = {'menu': menu}
# for cat in menu_categories:
# items = menu_items.filter(category_id=cat.id)
# category_items = []
# for item in items:
# category_items.append({
# "name": item.name,
# "price": float(item.price),
# "id": item.id
# })
# menu.append({"category": cat.name, "items": category_items})
except Exception:
print('failure')
return render(request, 'index.html', context)
Template
{% for category in menu %}
<div>{{ category.name }}</div>
{# {% for item in category.items %}#}
{# <div>{{ item.name }} | {{ item.price }}</div>#}
{# {% endfor %}#}
{% endfor %}

I think I have found my answer. This worked for me but I'm not sure if it's the 'preferred' way.
Python
def index(request):
if not request.user.is_authenticated:
return redirect('/login', {'message': None})
try:
menu_categories = MenuCategory.objects.all()
menu = []
for cat in menu_categories:
items = MenuCategoryItems.objects.filter(category_id=cat.id).all()
menu.append({'category': cat, 'items': items.values})
context = {'menu': menu}
except Exception:
print('failure')
return render(request, 'index.html', context)
Template
{% for category in menu %}
<div><strong>{{ category.category.name }}</strong></div>
{% for item in category.items %}
<div>{{ item.name }} - {{ item.price }}</div>
{% endfor %}
{% endfor %}

Related

Django - jinja syntax help - how do i pass the value of {{ title }} into the href url args?

so i have this html :
{% extends "encyclopedia/layout.html" %}
{% block title %}
{{ title }}
{% endblock %}
{% block body %}
<h1>{{ title }}</h1>
{% autoescape off %}
<p>{{ entry|safe }}</p>
{% endautoescape%}
<br>
<button type="button" name="button">Edit</button>
{% endblock %}
i want the url 'edit' to also get the value of the {{ title}} - instead of 'cat' ..i just used this value to test the rest of the code, but i don`t want to hard code it like that ...
here are my urls.py:
urlpatterns = [
path("", views.index, name="index"),
path("<str:title>", views.title, name="title"),
path("edit/<str:title>", views.edit, name="edit"),
path("error/", views.error, name="error"),
path("new/", views.new, name="new"),
]
this is the code in views.py :
def title(request, title):
entry = util.get_entry(title)
if entry == None:
return redirect("error")
entry_html = md.convert(entry)
return render(request, "encyclopedia/title.html", {
"entry": entry_html
})
def edit(request, title):
if request.method == "POST":
# get the changes from the user
form = EditEntryForm(request.POST)
if form.is_valid():
content = form.cleaned_data["content"]
# save the entry and redirect to the updated page
util.save_entry(title, content)
return redirect("title", title=title)
else:
redirect("edit", title=title)
entry = util.get_entry(title)
form = EditEntryForm(initial={'content': entry})
return render(request, "encyclopedia/edit.html", {
"form": form
})
as i said the code works as intended if i hard code the value of the title ....how do i get {{ title }} in the href url ?
You should just be able to pass it in as:
{% url 'edit' title %}
…or you may need to specify the argument name:
{% url 'edit' title=title %}
See this link in the documentation for more information.

Why do I get QuerySet object has no attribute user

I am working on a website using Django, where users can send friend
request to another user and I came up with an error on my code. Queryset object has no attribute user. According to my attached images, when I click on the follow button of the first user, all buttons changes text to 'cancel', but when I click on the button of the second user it does not change the text. I want when button clicked button text should be able to change for each users.
def home(request):
p = Profile.objects.exclude(user=request.user)
u = p.user
sent_request = FriendRequest.objects.filter(from_user=p.user)
button_status = 'none'
if p not in request.user.profile.friends.all()
button_status = 'not_friend'
if len(FriendRequest.objects.filter(from_uset=request.user).filter(to_user=p.user)) == 1:
button_status='add_request'
if len(FriendRequest.objects.filter(from_user=p.user).filter(to_user=request.user)) == 1:
button_status='cancel_request'
context = {'u':u, 'p':p, 'sent_request',:sent_request, 'button_status':button_status,}
return render(request, 'home.html', context)
{% for profile in p %}
{{ profile }}
{% if not request.user in u %}
{% if button_status == 'not_friend' %}
href = add url here
{% elif button_staus == 'cancel_request' %}
href = cancel request url here
{% endif %}
{% endif %}
{% endfor %}
#This only execute once on template, instead of twice (duplicate). How do I make this display twice in template?
{% for data in profile_and_button_status %}
{% data.0.user.username %}
#All codes here
{% endfor %}
{% if forloop.counter == 1 %}
{% for data in profile_and_button_status %}
#All codes here
{% data.0.user.username %}
{% endfor %}
{% endif %}
Because .exclude() will give you an queryset. So, you have to first iterate that queryset and get required user from there.
Because queryset it mean the list.
I think you have to pass zip of profiles list p and button status list button_status of a particular users as below...
def home(request):
p = Profile.objects.exclude(user=request.user)
all_profile_users = []
button_status_list = []
for user_obj in p:
u = user_obj.user
all_profile_users.append(u)
sent_request = FriendRequest.objects.filter(from_user=u)
friends = Profile.objects.filter(user=request.user, friends__id=user_obj.id).exist()
button_status = 'none'
if not friends
button_status = 'not_friend'
if len(FriendRequest.objects.filter(from_uset=request.user).filter(to_user=p.user)) == 1:
button_status='add_request'
if len(FriendRequest.objects.filter(from_user=p.user).filter(to_user=request.user)) == 1:
button_status='accept_request'
button_status_list.append(button_status)
context = {'u':all_profile_users, 'profile_and_button_status': zip(p, button_status_list), 'sent_request':sent_request}
Then, iterate profile_and_button_status in template as below...
{% for data in profile_and_button_status %}
<div class="row mb-2">
... YOUR DESIGN CODE ...
{{ data.0.user.username }}
{% if not request.user in u %}
{% if data.1 == 'not_friend' %}
... YOUR DESIGN CODE ...
{% elif data.1 == 'follow_request_sent' %}
... YOUR DESIGN CODE ...
And so on for other conditions.
For remove friend :
def remove_friend(request, id):
... YOUR LOGIC ...
frequest = FriendRequest.objects.filter(from_user=from_user, to_user=request.user).first()
if frequest is None:
frequest = FriendRequest.objects.filter(from_user=request.user, to_user=from_user).first()
user1 = frequest.to_user
user2 = from_user
... YOUR LOGIC ...

how to access items in a python dictionary by their name

I am creating a shopping cart with the code below
`
SESSION_COOKIE_AGE=300
SESSION_EXPIRE_AT_BROWSER_CLOSE =True
#require_POST
def cart_add(request, product_id):
product = get_object_or_404(Product, id=product_id)
quantity = request.POST.get('quantity')
product_id =str(product.id)
request.session[product_id] ={"product_name":str(product.title),"price":str(product.price),"quantity":quantity}
items =request.session.items()
print(items)
return render(request, 'cart/display_cart.html', {"products":request.session})`
Scenario one
when I pass items to my view like this=> return render(request, 'cart/display_cart.html', {"products":items}) and loop over it in the template using {% for item in products %}{{ item.product_name }} {% endfor %} nothing shows. but when I print it=> print(items) I get dict_items([('7', {'product_name': 'Tomatoe', 'price': '15.00', 'quantity': '3'})]) in my console. how can i print the items in my template
Scenario 2
when I pass request.session like this => return render(request, 'cart/display_cart.html', {"products":request.session}) and I do
{% for key, value in products.items %}
<li>{{value}}</li>
{% endfor %}
in my template I get
{'product_name': 'Tomatoe', 'price': '20.00', 'quantity': '1'}. how can I display the items in my template using product_name instead of key? Thank you
items is a nested dict
Use:
{% for k, item in products.items %}
{{ item.product_name }}
{% endfor %}

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 )

I can't figure why I'm getting unboundLocalError

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

Categories