Django: how to alter context dictionary based on request method called - python

I am passing a generator object results from fx_rates view to template like this:
def fx_rates(request):
if request.method != 'POST':
form = FxForm()
results=[]
else:
form = FxForm(request.POST)
if form.is_valid():
scraper = FxScraper()
scraper.from_currencies.append(form.cleaned_data['from_currencies'])
scraper.dates = form.cleaned_data['dates'].split(" ")
scraper.to_currency = form.cleaned_data['to_currency']
results = scraper.results()
context = {'form':form, 'results':results}
return render(request, 'map_assistant/fx_rates.html',context)
In the template for this view I am using the below to display results:
<ul>
{% for result in results %}
<li>{{result}}</li>
{% endfor %}
</ul>
(guess I can change it to {{next(results)}} once the issue described below is solved).
The problem is I don't know how to prevent "[]" from displaying when the view is called using GET method (see if clause above).
I cannot delete results=[] line because I will get an error when the view is called using GET.
Guess I would need to somehow remove results from context dictionary when the method is GET?

When the method is GET, try:
results = ""
This way, results is given a value of empty.

You can do something like this
def fx_rates(request):
show_div = 'none'
if request.method != 'POST':
form = FxForm()
results=[]
else:
show_div = 'block'
form = FxForm(request.POST)
if form.is_valid():
scraper = FxScraper()
scraper.from_currencies.append(form.cleaned_data['from_currencies'])
scraper.dates = form.cleaned_data['dates'].split(" ")
scraper.to_currency = form.cleaned_data['to_currency']
results = scraper.results()
context = {'form':form, 'results':results, 'show_div':show_div}
return render(request, 'map_assistant/fx_rates.html',context)
and your html will look like this
<div style="display:{{show_div}}">
<ul>
{% for result in results %}
<li>{{result}}</li>
{% endfor %}
</ul>
</div>
or simply check
{% if result %}
<ul>
{% for result in results %}
<li>{{result}}</li>
{% endfor %}
</ul>
{% else %}
{% endif %}

Related

Handling three forms on the same page with one having multiple instances - Django

I am working on an Invoice that has three forms of which one has multiple instances. The first two forms each has one instance and the last one can accommodate as many as the user wants. meaning the user will be adding instances of the last form using JavaScript. With the below code I am getting an error that is saying that the fields of the first two forms are missing data cause whenever the user add extra fields to the last form Django thinks I am also adding extra forms to the other two forms. Please help
This is an example of the POST data with two extra fields for the last form:
form-TOTAL_FORMS '2'
form-INITIAL_FORMS '0'
form-MIN_NUM_FORMS '0'
form-MAX_NUM_FORMS '1000'
form-0-number '215'
form-0-customer '4'
form-0-sales_person '1'
form-0-product '2'
form-0-quantity '20'
form-0-price '20'
form-1-product '2'
form-1-quantity '40'
form-1-price '50'
This is my view.py
def createInvoice(request):
invoiceFormSet = formset_factory(InvoiceForm)
retailPriceFormSet = formset_factory(RetailPriceForm, extra=2)
saleItemFormSet = formset_factory(SaleItemForm)
if request.method == 'POST':
invoiceform = invoiceFormSet(request.POST or None)
retailPriceform = retailPriceFormSet(request.POST or None)
saleItemform = saleItemFormSet(request.POST or None)
if invoiceform.is_valid() and saleItemform.is_valid():
for si in saleItemform:
saleItem = si.save()
saleItem_id = get_object_or_404(SaleItem, id=saleItem.id)
for i in invoiceform:
inv = i.save(commit=False)
inv.sale_item = saleItem_id
inv.save()
for rp in retailPriceform:
retailPrice = rp.save(commit=False)
retailPrice.sale_item = saleItem_id
retailPrice.save()
return HttpResponse(retailPriceform.cleaned_data)
context = {'invoiceFormSet':invoiceFormSet,'retailPriceFormSet':retailPriceFormSet, 'saleItemFormSet':saleItemFormSet}
return render(request, 'createInvoice.html',context)
This is my template
{% extends 'base.html' %}
{% block title %} Create Invoice {% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{saleItemFormSet.as_p}}
{{invoiceFormSet.as_p}}
{{ retailPriceFormSet.management_form }}
{% for form in retailPriceFormSet %}
<div>{{form.as_p}}</div>
{% endfor %}
<button type="submit" class="btn btn-normal btn-main">Save</button>
</form>
{% endblock %}
Use a prefix to use multiple formset in a single view.
Example:
if get:,
article_formset = invoiceFormSet(prefix='articles')
book_formset = retailPriceFormSet(prefix='books')
if post:
article_formset = invoiceFormSet(request.POST, request.FILES, prefix='articles')
book_formset = retailPriceFormSet(request.POST, request.FILES, prefix='books')
reference: Django Documentation

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 ...

form.as_hidden doesn't pass values to POST

My form has initial values in it. I use form.as_hidden to hide the values and pass those values through a POST request. However, the hidden values are not passing through. Is there a way through this?
views.py
def car_detail_view(request, id):
if request.method == "POST":
form = CarForm(request.POST)
print(form.is_valid())
if form.is_valid():
car_save = form.instance
get_car = Car.objects.get(number_plate=car_save.number_plate)
get_car.available = False
get_car.save()
return redirect('/')
else:
print(form.errors)
else:
car = Car.objects.get(id=id)
form = CarForm(initial={'brand':car.brand, 'number_plate':car.number_plate, 'price':car.price,
'available':car.available})
args = {
'car':car,
'form':form
}
return render(request, 'map/confirmation.html', args)
confirmation.html
<h1>Confirmation of Booking</h1>
{% block content %}
<p>Brand: {{ car.brand }}</p>
<p>Number Plate: {{ car.number_plate }}</p>
<p>Price: {{ car.price }}</p>
<p> Are you sure you want to book? <p>
<form class="" method="post">
{% csrf_token %}
{{ form.as_hidden }}
<input type="submit" value="Book {{ car.brand }}">
</form>
{% endblock %}
Error
<ul class="errorlist"><li>brand<ul class="errorlist"><li>This field is required.</li></ul></li><li>number_plate<ul class="errorlist"><li>This field is required.</li></ul></li><li>price<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
Django doesn't have a form.as_hidden method. Therefore {{ form.as_hidden }} will render as the empty string '' in your template.
You can use the as_hidden method for individual form fields.
{{ form.number_plate.as_hidden }}
If you use values from hidden fields, you might need to add code to prevent the user altering the field values (e.g. with their browser's developer tools). However, in your case you don't need to get the values from the form, you can fetch them from the database.
def car_detail_view(request, id):
if request.method == "POST":
car = Car.objects.get(id=id)
car.available = False
car.save()
return redirect('/')
else:
car = Car.objects.get(id=id)
args = {
'car':car,
}
return render(request, 'map/confirmation.html', args)
Once you've got this working, you might want to think about what happens if two users try to book the same car at once.

Not able to print the dictionary returned from view function in template

my view function returns a dictionary . I am trying to print the dictionary in my template. But nothing gets printed. It prints application/json instead of printing the values of dictionary .Can anyone help me solve this ?
def display(request):
if request.method == 'POST' or 'GET' :
form = displayForm(request.POST or request.GET or None)
val = request.POST.get('ser')
form.fields['ser'].choices = [(val, val)]
if form.is_valid():
#context = RequestContext(request)
grp = request.POST.get('grp_name')
env1 = request.POST.get('env')
serv = request.POST.get('ser')
res ={}
res = check_expiry(request.GET,grp,env1,serv)
print res
return render_to_response('certificate_expiry/display.html', {'form':form ,'res': res} )
else:
form = UrlForm()
return render(request,'certificate_expiry/display.html',{'form':form})
## the html template
<div>
{% for k,v in res.iteritems %}
<li> {{ v }} </li>
{% endfor %}
</div>
In django template you use .items to access dictionary:
{% for k,v in res.items %}
<li> {{ v }} </li>
{% endfor %}
You aren't passing res to the template response if form.is_valid() returns false. Instead you are only passing the form back to the template.
This is most likely why you aren't seeing your iteration get printed out.
You need .items rather than .iteritems in django templates.
Also, you can try changing your render_to_response function into a render function. (I prefer the latter).
return render(request, 'certificate_expiry/display.html', {'form':form,'res':res})

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