I use four arguments in quoted.html, but when I try to pass them to from my application.py, there is an error:
TypeError: render_template() takes 1 positional argument but 4 were given
#app.route("/quote", methods=["GET", "POST"])
#login_required
def quote():
"""Get stock quote."""
#Look up for the stock symbol when user requires
if request.method == "POST":
#Check the symbol is none
if lookup(request.form.get("symbol")) == None:
return apology("The company's stock is not found!", 403)
#Show the stock result including name of the company and the stock price
else:
name = lookup(request.form.get("symbol"))["name"]
symbol = lookup(request.form.get("symbol"))["symbol"]
price = usd(lookup(request.form.get("symbol"))["price"])
return render_template("/quoted", name, symbol, price)
#Display the quote interface when user requires
else:
return render_template("quote.html")
Here is my quoted.html
{% extends "layout.html" %}
{% block title %}
Quote
{% endblock %}
{% block main %}
<form action="/quoted" method="get">
<fieldset>
<div class="form-group">
<h1> A share of {{ name }} ({{ symbol }}) costs {{ price }}</h1>
</div>
</fieldset>
</form>
{% endblock %}
Here is the return for the lookup function
def lookup(symbol):
"""Look up quote for symbol."""
# Parse response
try:
quote = response.json()
return {
"name": quote["companyName"],
"price": float(quote["latestPrice"]),
"symbol": quote["symbol"]
}
The render_template() takes one positional argument which is the html file. The rest of the data should be passed as keyword arguments.
Here is the documentation about it:
https://flask.palletsprojects.com/en/1.0.x/api/#flask.render_template
So you could do something like:
return render_template("quoted.html", name=name, symbol=symbol, price=price)
Or make one data structure with your data (dict, list) and work with it in your html
Related
So guys I have 2 problems.
I am pretty new in Django and Python. I build a page with a form and I can input data in those fields, I receive the data do some operation with it and pass back other variables which are shown in a div under the form field after a button was clicked. It is actually working but the page refreshes so I can see the result for only one second. Second Problem is that I have to click twice on the button so it shows me the right result. Example: First button click Result shows x, then I use other input and click button, result shows x again. After I click the button again it shows the right result, how do I fix this problem aswell.
And do you have suggestions how to make this system better?
I am already really thankful for your help.
My view file:
from django.shortcuts import render,redirect
from django.http import HttpResponse
from pages.forms import NameForm
import requests
# Create your views here.
def home_view(request,*args,**kwargs):
api_key = "RGAPI-9b573263-7b5a-433e-9e82-a973f5db4749"
name = ""
tier = ""
rank = ""
lp = 0
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
summoner = form.cleaned_data["summoner"]
region = form.cleaned_data["region"]
url = "https://" + region + ".api.riotgames.com/lol/summoner/v4/summoners/" \
"by-name/" + summoner + "?api_key=" + api_key
response = requests.get(url).json()
id = response["id"]
name,tier,rank,lp = ranklookup(id,region,api_key)
return render(request,"home.html",{'form' : form,'rank':rank, 'tier' : tier, 'lp' : lp, 'name' : name})
def ranklookup(id,region,api_key):
rankurl = "https://" + region + ".api.riotgames.com/lol/league/v4/entries/by-summoner/" + id + "" \
"?api_key=" + api_key
rankResponse = requests.get(rankurl).json()
if rankResponse[0]["queueType"] == "RANKED_SOLO_5x5":
name = rankResponse[0]["summonerName"]
tier = rankResponse[0]["tier"]
rank = rankResponse[0]["rank"]
lp = str(rankResponse[0]["leaguePoints"])
else:
name = rankResponse[0]["summonerName"]
tier = rankResponse[1]["tier"]
rank = rankResponse[1]["rank"]
lp = str(rankResponse[1]["leaguePoints"])
return name,tier,rank,lp
And my HTML file:
{% extends 'base.html' %}
{% block content %}
<h2> League of Legends Rank lookup</h2> <br>
<div id ="ranklookup">
<form id="myform" method="post"> {% csrf_token %}
{{ form }}
</form>
<button onclick="showDiv()" type="submit" id="but" form="myform" value="button">Submit</button><br><br>
<div id="showRank">
This will get replaced
</div>
<script type="text/javascript">
function showDiv(){
var tier = ' {{ tier }}';
var rank = ' {{ rank }} ';
var name = ' {{ name }} ';
var lp = {{ lp}};
document.getElementById("showRank").innerHTML = "Name: " + name + "</br> Rank: " + tier + rank + "</br> LP: " + lp;
}
</script>
{% endblock %}"
If you are not trying to actually reload the page entirely, you should look for how to handle AJAX form in django (i.e. in this previous answer).
Otherwise, you can just change your views.py code as follows:
def home_view(request,*args,**kwargs):
api_key = "RGAPI-9b573263-7b5a-433e-9e82-a973f5db4749"
name = ""
tier = ""
rank = ""
lp = 0
if request.method == 'GET':
form = NameForm(auto_id='myform')
return render(request,"home.html",{'form' : form, 'showdiv': False})
if request.method == 'POST':
form = NameForm(request.POST)
if form.is_valid():
summoner = form.cleaned_data["summoner"]
region = form.cleaned_data["region"]
url = "https://{}.api.riotgames.com/lol/summoner/v4/summoners/by-name/{}?api_key={}".format(region, summoner, api_key)
response = requests.get(url).json()
id = response["id"]
name, tier, rank, lp = ranklookup(id, region, api_key)
return render(request, "home.html", {
'form': form,
'rank': rank,
'tier': tier,
'lp': lp,
'name' : name,
'showdiv': True })
and the template (which now does not use javascript at all):
{% extends 'base.html' %}
{% block content %}
<h2> League of Legends Rank lookup</h2> <br>
<div id ="ranklookup">
<form id="{{ form.auto_id }}" method="post">
{% csrf_token %}
{{ form }}
</form>
<button type="submit" id="but" form="{{ form.auto_id }}" value="button">
Submit</button><br><br>
{% if showdiv %}
<div id="showRank">
Name: {{ name }}<br>
Rank: {{ tier }}{{ rank }}<br>
LP: {{ lp }};
</div>
{% endif %}
{% endblock %}
How to add parameters in a url in render method - Django?
I'm having difficulty adding pagination to a search result.
On the first page the result is shown perfectly, but from the second page onwards, the search parameter no longer exists.
thank you.
def get(self, request):
clientes = Cliente.objects.filter(
Q(nome__icontains=request.GET['nome']))
formPesquisa = FormPesquisaCliente()
paginator = Paginator(clientes, 40)
page = request.GET.get('page')
clientes = paginator.get_page(page)
response = render(request, 'cliente/index.html', {
'clientes': clientes,
'formPesquisa': formPesquisa})
response['Location'] += '?nome=' +request.GET['nome']
return response
What are you missing is that when you have filtered data from the queryset and its paginated so obviously to view the next page you need to maintain the state by passing the same filter object nome. So the url should look something like this.
http://localhost:8000/clients/?page=2&nome=something
def get(self, request):
abc = request.GET.get['nome']) #<--- variable to get value from view
clientes = Cliente.objects.filter(
Q(nome__icontains=abc)) #<-- pass the abc variable here
formPesquisa = FormPesquisaCliente()
paginator = Paginator(clientes, 40)
page = request.GET.get('page')
clientes = paginator.get_page(page)
response = render(request, 'cliente/index.html', {
'clientes': clientes,
'formPesquisa': formPesquisa,
'abc':abc}) #<-- passing the abc variable to view to maintain the state of your filter.
response['Location'] += '?nome=' +request.GET['nome']
return response
Example Pagination Code:
<div class="pagination">
<span class="step-links">
{% if clients.has_previous %}
« first
{% if nome %}
previous
{% else %}
previous
{% endif %}
{% endif %}
<span class="current">
Page {{ clientes.number }} of {{ clientes.paginator.num_pages }}.
</span>
{% if clientes.has_next %}
{% if nome %}
next
last »
{% else %}
next
last »
{% endif %}
{% endif %}
</span>
</div>
The following code is from an multi-vendor ecommerce portal. We need to display different shipping methods according to the store(or vendor) on the checkout summary page.
However even though I get correct queryset while print i.e Store 1 has Test Rest of World Shipping method and Store 2 has UPC and DHL, the rendered form shows incorrect values -
#########################################################
class ShippingCountryChoiceField(forms.ModelChoiceField):
widget = forms.RadioSelect()
def label_from_instance(self, obj):
price_html = format_price(obj.price.gross, obj.price.currency)
label = mark_safe('%s %s' % (obj.shipping_method, price_html))
return label
class ShippingMethodForm(forms.Form):
def __init__(self, country_code, *args, **kwargs):
stores = kwargs.pop('stores')
super(ShippingMethodForm, self).__init__(*args, **kwargs)
for count, store in enumerate(stores, start=1):
method_field = ShippingCountryChoiceField(
queryset=ShippingMethodCountry.objects.select_related(
'shipping_method').order_by('price').filter(shipping_method__store=store),
label=pgettext_lazy('Shipping method form field label', 'Shipping method for %s' % store),
required=True)
if country_code:
queryset = method_field.queryset
method_field.queryset = queryset.unique_for_country_code(country_code)
if self.initial.get('method') is None:
method_field.initial = method_field.queryset.first()
method_field.empty_label = None
self.fields['method_%d' % count] = method_field
print [q.queryset for q in self.fields.values()]
###################################################
#load_checkout
#validate_voucher
#validate_cart
#validate_is_shipping_required
#validate_shipping_address
#add_voucher_form
def shipping_method_view(request, checkout):
country_code = checkout.shipping_address.country.code
stores = checkout.cart.lines.values_list('variant__product__store', flat=True)
stores = Store.objects.filter(id__in=stores)
print checkout.shipping_method
shipping_method_form = ShippingMethodForm(
country_code, request.POST or None, initial={'method': checkout.shipping_method},
stores=stores)
if shipping_method_form.is_valid():
for count, store in enumerate(stores):
checkout.shipping_method[store] = shipping_method_form.cleaned_data['method_%s' % count]
return redirect('checkout:summary')
print [q.queryset for q in shipping_method_form.fields.values()]
return TemplateResponse(request, 'checkout/shipping_method.html', context={
'shipping_method_form': shipping_method_form, 'checkout': checkout})
##############################################################
{% extends "checkout/details.html" %}
{% load i18n %}
{% load gross from prices_i18n %}
{% load bootstrap_form from bootstrap3 %}
{% block forms %}
<h3>{% trans "Shipping address" context "Checkout shipping address title" %}</h3>
{% include "userprofile/snippets/address-short.html" with address=checkout.shipping_address only %}
<p>{% trans "Select other address" %}</p>
<hr>
<form method="post" novalidate>
{% csrf_token %}
{% bootstrap_form shipping_method_form show_label=True %}
<p class="text-md-right">
<button type="submit" class="btn primary">
{% trans "Continue" context "Checkout shipping method primary action" %}
</button>
</p>
</form>
{% endblock %}
I believe the problem is that you are instantiating the widget in the field definition. This could cause state to be shared between different fields. Try changing it to:
class ShippingCountryChoiceField(forms.ModelChoiceField):
widget = forms.RadioSelect
...
I have been having some trouble with /quote in PSET 7 of CS50. Every time I go into the CS50 finance site, it returns:
AttributeError: 'NoneType' object has no attribute 'startswith'
I am not sure what it means, nor how to fix it. It seems to be automatically going to 'None' in the lookup function, but I am not sure why. If someone could help me, I would really appreciate it!
This is my quote part of application.py:
#app.route("/quote", methods=["GET", "POST"])
#login_required
def quote():
"""Get stock quote."""
if request.method == "POST":
symbol = request.args.get("symbol")
quote = lookup(symbol)
return render_template("quoted.html", name=quote)
else:
return render_template("quote.html")
This is my helpers.py, which is not supposed to be changed:
def lookup(symbol):
"""Look up quote for symbol."""
# reject symbol if it starts with caret
if symbol.startswith("^"):
return None
# reject symbol if it contains comma
if "," in symbol:
return None
# query Yahoo for quote
# http://stackoverflow.com/a/21351911
try:
url = "http://download.finance.yahoo.com/d/quotes.csv?f=snl1&s={}".format(symbol)
webpage = urllib.request.urlopen(url)
datareader = csv.reader(webpage.read().decode("utf-8").splitlines())
row = next(datareader)
except:
return None
# ensure stock exists
try:
price = float(row[2])
except:
return None
# return stock's name (as a str), price (as a float), and (uppercased) symbol (as a str)
return {
"name": row[1],
"price": price,
"symbol": row[0].upper()
}
Finally, this is my quote.html:
{% extends "layout.html" %}
{% block title %}
Quote
{% endblock %}
{% block main %}
<form action="{{ url_for('quote') }}" method="post">
<fieldset>
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="symbol" placeholder="symbol" type="symbol"text"/>
</div>
<div class="form-group">
<button class="btn btn-default" type="submit">Search for Quote</button>
</div>
</fieldset>
</form>
{% endblock %}
That error would occur when there's no "symbol" parameter in the request.
symbol = request.args.get("symbol")
quote = lookup(symbol)
Because it's not present, .get(...) will return None, and when you call lookup(None) it will try to run the following line, with symbol as None:
if symbol.startswith("^"):
Which means you're trying to do None.startswith(...), explaining the error you see.
You could check for the case where symbol is missing/None and display an error message.
symbol = request.args.get("symbol")
if symbol:
quote = lookup(symbol)
return render_template("quoted.html", name=quote)
else:
return render_template("missing_symbol.html")
Or you could just ignore it: if there's no symbol, the request is probably invalid, and you can accept that it causes an error.
I managed to find the answer, I was supposed to put:
symbol = request.form.get("symbol") instead of:
symbol = request.args.get("symbol").
I want users to be able to query my database via several different parameters (name, year, etc), dynamically add more fields, and join them with boolean operators; the net result would be something like "year = 1900 AND name = chicken AND location = San Francisco." I think I'm doing something wrong, since it's not returning anything, even when I try just one field with a value that I know matches some data (e.g., I can get objects back when I use .filter() from the Django shell). Anyone know how I can fix it?
Relevant view (ignore the sloppy indentation, I didn't want to go through and fix all of it, but it's right in my actual code):
class BaseSearchFormSet(BaseFormSet):
def clean(self):
if any(self.errors):
return self.errors
queries = []
valid_courses = ["appetizer","main","dessert"]
valid_period = re.compile(r'\d\d\d0-\d\d\d5|\d\d\d5-\d\d\d0')
valid_year = re.compile(r'\d{4}')
multi_rows = ["year","period","course"]
for x in xrange(0,self.total_form_count()):
form = self.forms[x]
query = form.cleaned_data.get("query")
row = form.cleaned_data.get("row")
if query in queries and row not in multi_rows:
raise forms.ValidationError("You're already searching for %s.")
queries.append(query)
if row == "course" and query.lower() not in valid_courses:
raise forms.ValidationError("%s is not a valid course option."%(form.cleaned_data["query"]))
if row == "period" and not re.match(valid_period,query):
raise forms.ValidationError("%s is not a properly formatted period. Valid five-year periods span either the first or second half of a decade. For example: 1910-1915, 1925-1930."%(form.cleaned_data["query"]))
if row == "year" and not re.match(valid_year,query):
raise forms.ValidationError("Please enter a four-digit year.")
def search(request):
errors = []
searchFormSet = formset_factory(F.SearchForm, extra=1,formset=BaseSearchFormSet)
if request.GET:
formset = searchFormSet(request.GET)
forms = []
if formset.is_valid():
for x in xrange(0,formset.total_form_count()):
form = {}
form["row"]= formset[x].cleaned_data.get("row",None)
form["query"] = formset[x].cleaned_data.get("query",None)
form["bools"] = formset[x].cleaned_data.get("bools",None)
if form["query"]:
q = form["query"]
else:
errors.append("no query found")
if form["row"]:
row = form["row"]
else:
errors.append("no row found")
filter_keys = {"dish_name":Q(dish__name__icontains=q),
"regex":Q(dish__full_name__regex=r'%s'%(q)),
"course":Q(dish__classification=q.lower()),
"year":Q(page__menu_id__year__exact=q),
"period":Q(page__menu_id__period__exact=q),
"location":Q(page__menu_id__location__icontains=q),
"restaurant":Q(page__menu_id__restaurant__icontains=q)}
forms.append(form)
final_query=Q()
def var_reduce(op,slice):
if op == "and":
return reduce(lambda x,y: x & y,slice)
elif op == "or":
return reduce(lambda x,y: x | y,slice)
for x in xrange(len(forms)):
try:
try:
if final_query:
slice = [final_query,filter_keys[forms[x]["row"]],filter_keys[forms[x+1]["row"]]]
else:
slice = [filter_keys[forms[x]["row"]],filter_keys[forms[x+1]["row"]]]
final_query = var_reduce(forms[x]["bools"],slice)
except IndexError:
if final_query:
slice = [final_query,filter_keys[forms[x]["row"]]]
else:
slice = [filter_keys[forms[x]["row"]]]
final_query = var_reduce(forms[x]["bools"],slice)
items = MenuItem.objects.filter(final_query)
return render_to_response("search_results.html",{"items":items,"formset":formset})
except KeyError as e:
errors.append(e)
formset = searchFormSet(None)
return render_to_response("search_page.html",{"errors":errors,"formset":formset})
else:
formset = searchFormSet(None)
return render_to_response("search_page.html",{"errors":errors,"formset":formset})
else:
formset = searchFormSet(None)
return render_to_response("search_page.html",{"formset":formset})
models:
from django.db import models
class MenuItem(models.Model):
def format_price(self):
return "${0:0<4,.2f}".format(float(self.price))
def __unicode__(self):
return self.dish
dish=models.OneToOneField('Dish',to_field='mk')
price=models.CharField(max_length=5,blank=True)
page=models.OneToOneField('MenuPage')
mk=models.CharField(max_length=10,unique=True)
formatted_price = property(format_price)
class Menu(models.Model):
def period(self):#adapted from http://stackoverflow.com/questions/2272149/round-to-5or-other-number-in-python
try:
p=int(10*round(float(int(self.year))/10))
if p < self.year:
return "%s-%s"%(p,p+5)
else:
return "%s-%s"%(p-5,p)
except (ValueError,TypeError):
return ""
def __unicode__(self):
if self.restaurant:
return self.restaurant
else:
return self.mk
restaurant=models.TextField(unique=False,blank=True,null=True)
year=models.CharField(max_length=4,unique=False,blank=True,null=True)
location=models.TextField(unique=False,blank=True,null=True)
status=models.CharField(unique=False,max_length=20)
mk=models.CharField(max_length=8,unique=True,primary_key=True)
period=property(period)
language = models.CharField(unique=False,max_length=30)
#objects=MenuManager()
class MenuPage(models.Model):
mk=models.CharField(max_length=10,unique=True)
menu_id=models.OneToOneField("Menu",to_field='mk')
#objects=MenuPageManager()
class Dish(models.Model):
def __unicode__(self):
return self.name
full_name = models.TextField()
name=models.CharField(unique=True,max_length=255)
mk=models.CharField(max_length=10,unique=True)
class Classification(models.Model):
def __unicode__(self):
if self.classification:
return self.classification
else:
return "none"
dish=models.OneToOneField('dish',to_field='name')
classification=models.CharField(unique=False,max_length=9)
mk=models.CharField(max_length=10,primary_key=True)
html of my search page:
{% extends "base.html" %}
{% block style %}
<link rel="stylesheet" type="text/css" href="/static/search_style.css" />
{% endblock %}
{% block java %}
<script type="text/javascript" src="/static/searches.js"></script>
{% endblock %}
{% block title %}Search{% endblock %}
{% block head %}Search{% endblock %}
{% block content %}
{% autoescape off %}
<div id="searches">
<form id="search" action="" method="get">
<table border="0" cellpadding="0" cellspace="0">
<tbody class="search">
{% for form in formset.forms %}
<tr>
<td class="row">{{ form.row }}</td>
<td class="query">{{ form.query }}</td>
<td class="bool">{{ form.bools }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ formset.management_form }}
<input type="submit" value="Submit" id="submit">
</form>
</div>
{% if formset.errors or errors %}
<div id="errors">
<h3>The following errors were encountered while trying to submit your search:</h3>
{% for x,y in formset.errors.items %}
<p>{{ x }} : {{ y }}</p>
{% endfor %}
{{ errors }}
</div>
{% endif %}
<div id="notes">
<p>Searching by dish names, locations, and restaurants is case-insensitive.</p>
<p>Searching by course uses case-insensitive exact matching. Valid courses are Appetizer, Main, and Dessert.</p>
<p>Years should be entered YYYY. Five-year periods span either the first or second half of a decade, and should be entered YYYY-YYYY. Example valid five-year periods are 1900-1905, 1995-2000, etc.</p>
<p>Regular expression search follows MySQL regular expression syntax, as described here.</p>
</div>
{% endautoescape %}
<br /><br /><br /><br /> <br /><br /><br />
{% endblock %}
{% block footer %}
<div id="warning">
<p>NOTE: This site and the information it contains are still in development. Some information may be missing or inaccurate.</p>
</div>
<div class="credits">
Created and maintained by Sam Raker and Rachel Rakov
<br />
Data graciously provided by What's on the Menu?
</div>
{% endblock %}
Sorry, my original post was more than you needed. To clarify, all you should need to do is:
Q(year__icontains=year_input_variable) | Q(city__icontains=city_input_variable) & Q(name__icontains=name_input_variable)
Use & for and, | for or.
What I had posted earlier is for if a query contains multiple words, it would either check to see if all of the words matched using operator.and or if any of the words matched using operator.or.