Display data from database on a separate html page - python

I am not sure if this question has been answered before, I tried to find other answers, but was unsuccessful, it may be that I am not put the right searches.
I am trying to complete the CS50 Web Finance project, and I'm running into a wall with the very beginning of the portfolio page. More specifically, I'm having trouble extracting data from my finance.db on my python page and then displaying it on my portfolio.html page. I think that I have successfully extracted the username from my finance.db, as shown in the code below.
#app.route("/")
#login_required
def index():
"""Show portfolio of stocks"""
# look up the current user
user = db.execute("SELECT username FROM users WHERE id = :id", id=session["user_id"])
return render_template("portfolio.html")
But, no matter what I do in my portfolio.html I cannot get it so say "Hello [username]" is just keeps saying "Hello undefined"
{% extends "layout.html" %}
{% block title %}
Home
{% endblock %}
{% block main %}
<p>Hello {{ [username] }}</p>
{% endblock %}
I'm sure this is something that most people are able to get, but I'm still kind of new and I just don't understand. I've tried to look up resources, but the only thing I've found is other people's code, and I don't want to copy and paste, I want to learn how to do it properly, so any help would be greatly appreciated. Thank you so much in advance.

In flask, you just need to 'pass' the username info to your portfolio.html page via render_template.
So, like:
return render_template("portfolio.html", username=user)
Then display this in the template with:
<p>Hello {{username}}</p>

Related

CS50 Finance - can't get quote from form?

I'm working my way through CS50's finance problem set. (https://cs50.harvard.edu/x/2020/tracks/web/finance/)
In this task a user should be able to submit a form on a html page to request the current price of a share in a company.
Below is what I've made so far for the quote route (using python and flask):
#app.route("/quote", methods=["GET", "POST"])
#login_required
def quote():
"""Get stock quote."""
if request.method == "POST":
quote = lookup(request.form.get("requested_share"))
if quote == None:
return apology("No share found for this symbol")
return render_template("quoted.html", quote=quote)
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("quote.html")
And here is what I've written for the "quote" html page:
{% extends "layout.html" %}
{% block title %}
Quote
{% endblock %}
{% block main %}
<form action="/quote" method="post">
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="requested_share" placeholder="e.g. TSLA" type="text" />
</div>
<button class="btn btn-primary" type="submit">Request Quote</button>
</form>
{% endblock %}
Finally, here is what I've got for the "quoted" html page (where the user should be taken if their request for a quote is successful):
{% extends "layout.html" %}
{% block title %}
Quoted
{% endblock %}
{% block main %}
<p>A share of {{ quote["name"] }} costs {{ quote["price"] }}</p>
{% endblock %}
However, when I try it out, it seems to be acting as though the user hasn't typed anything in. Instead of going to the quoted.html page, it goes to the apology page from this part of the code:
if quote == None:
return apology("No share found for this symbol")
Can anyone help with this? I've been staring at it for ages, still can't see where it's going wrong. I thought it might have been something to do with the API Key, though I've followed the instructions on the page
I assumed it might have to do with the GET calls returning a None at the end
DEBUG:urllib3.connectionpool:https://cloud-sse.iexapis.com:443 "GET /stable/stock/AAPL/quote?token=[your_api_key] HTTP/1.1" 200 None
So, I've tried plugging in your code into mine and swapping out things to make sure I could recreate what you are experiencing:
if not quote:
// instead of
if quote == None:
It happened once or twice but it turned out to be wrongly entered symbols e.g. 'APPL' instead of 'AAPL' for apple stock.
I had this issue initially when nothing was being returned from the api calls through the lookup() method. If you don't see a message in your console when you lookup a stock symbol like this:
"GET /stable/stock/AAPL/quote?token=[your_api_key] HTTP/1.1" 200 None
or
"GET /stable/stock/AAPL/quote?token=[your_api_key] HTTP/1.1" 404 None
When GET returns 404 then the quote == None triggers and you get the call to the apology() function.
So if you don't see anything similar to thos lines, then that means no calls are being sent to the IEX api. I'm not sure if it's some sorta security feature (or even if that's the case, like someone else commented ensure that your api key is working properly just to be sure) but I tried making the call using the postman app by manually inserting the stock symbol and api key and after that it started working. Postman is an app for building and testing apis, you can get it here - link to Postman
This is what the result looks like in Postman:
So try that if you haven't and then checking if your console in the CS50 ide is showing the calls being made.

Django wildcard in request.path

don't know much about django but trying to resolve something quickly for a client, they want some content to only show up on certain pages but the code is nav (used across all pages) hence was trying to add:
{% if request.path == '/*/projects/' %} something {% endif %}
but this doesn't work. Each project has a unique number, hence unique url, but I cannot do:
{% if 'projects' in request.path %} something {% endif %}
as some pages on the site contain "projects" in the url where we do not want "something" showing. Hence I wanted to use a simple * as wildcard in url, but this does not work.
Many thanks!

What is the technique of presenting again form which contains error?

Using Flask + Flask-Classy + WTForms, I am trying to implement a route, such that, when user having error in their input form, application will presented again that form to the user, complete with what user already filled. This way, user didn't have to worried of typing again all the form input : (s)he can correct the errors.
These are code from my class UserView(FlaskView) which is a flask-classy class :
def post(self):
form = forms.UserForm(request.form)
if form.validate():
if form.id.data=='':
user = models.Users()
form.populate_obj(user)
user.id = None
db.session.add(user)
else:
user = models.Users.query.get(form.id.data)
form.populate_obj(user)
db.session.commit()
return redirect(url_for('UserView:index'))
else:
return redirect(url_for('UserView:validation_error', error_form=form))
def validation_error(self, error_form):
return render_template('users/form.html', form=error_form)
You can see that in this attempt, I try to create another method : validation_error, with argument --that in my expectation-- should contain all the form data. But, turns out I got Jinja2 exception, UndefinedError: 'unicode object' has no attribute 'hidden_tag'. As I inspect it, after having been in validation_error, that form object is somewhat changed from UserForm into somekind of unicode object.
Any suggestion? Or, is it that I try to solve this problem in a wrong direction?
EDIT 1 : Elaborate more on how to display the error
As #Rawrgulmuffins added an answer on how to display the error inline (which I think is the best way, as the page will not get jumpy), here I pasted the code in my layout.html that list all the errors. It may not as good as an inline error display, but I think it's the easiest way there is. First I do an if for the form object, as this code is also exist for all page (I maybe refactored it even more)
{% if form %}
{% for error in form.errors %}
<div class="alert alert-error">
<a class="close" data-dismiss="alert">×</a>
<strong>{{ form[error].label }} {{form.errors[error][0] }}</strong>
</div>
{% endfor %}
{% endif %}
Screenshot given :
I'm adding my answer to expand swdev's answer for anyone that doesn't understand why he can just render the template and show the errors.
Each field in a form contains a errors attribute that will be populated after validation (after form.validate()). These errors will be contained in the form object. So you can say something like,
{% if field.errors %}
<span class="error help-inline">{{ field.errors|join(', ') }}</span>
{% endif %}
example source file. This is very nifty because you can just say
return render_template('users/form.html', form=form)
and if it's the first render get a error free form and display errors only on the second rendering of the form.
I'm not sure why Swdev attempt to redirect the form caused it to be manipulated right now. I'm looking through the Werkzeug code right now to see if I can find an answer.
Ow, how stupid I am.
Just after I got a rest for a while, I found a simple answer, that is, instead of passing to another view method :
else:
return redirect(url_for('UserView:validation_error', error_form=form))
I can easily render the template :
return render_template('users/form.html', form=form)
That simple ;)

Django template check for empty when I have an if inside a for

I have the following code in my template:
{% for req in user.requests_made_set.all %}
{% if not req.is_published %}
{{ req }}
{% endif %}
{% empty %}
No requests
{% endfor %}
If there are some requests but none has the is_published = True then how could I output a message (like "No requests") ?? I'd only like to use Django templates and not do it in my view!
Thanks
Even if this might be possible to achieve in the template, I (and probably many other people) would advise against it. To achieve this, you basically need to find out whether there are any objects in the database matching some criteria. That is certainly not something that belongs into a template.
Templates are intended to be used to define how stuff is displayed. The task you're solving is determining what stuff to display. This definitely belongs in a view and not a template.
If you want to avoid placing it in a view just because you want the information to appear on each page, regardless of the view, consider using a context processor which would add the required information to your template context automatically, or writing a template tag that would solve this for you.

How do I pass template context information when using HttpResponseRedirect in Django?

I have a form that redirects to the same page after a user enters information (so that they can continue entering information). If the form submission is successful, I'm returning
HttpResponseRedirect(request.path)
which works fine. However, I'd also like to display some messages to the user in this case (e.g., "Your data has been saved" at the top of the screen). If I weren't redirecting, I'd just return these messages in the context dictionary. With the redirect, however, I can't do this.
So how can I pass template context information when using HttpResponseRedirect?
What I'm trying to do seems like it would be incredibly common, so please excuse me if I'm missing something obvious.
For the sake of completion and future reference, you can now use the messages framework. After you install it:
views.py
from django.contrib import messages
def view(request):
# your code
messages.success(request, "Your data has been saved!")
HttpResponseRedirect(request.path)
template.html
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
if you are using auth and have a logged in user you could:
http://docs.djangoproject.com/en/dev/topics/auth/#django.contrib.auth.models.User.message_set.create
GET params are also hackable. The querystring, as mentioned in other answers, could be used.
I think the most preferred way would be to use the sessions framework. That way you can load up whatever you want in the context and get
{{ request.session.foo }}
foo could be the message or you could do:
{% ifequal request.session.foo 1 %} Nice work! {% else %} Almost! {% endifequal %}
and other fun stuff.
http://docs.djangoproject.com/en/dev/topics/http/sessions/#using-sessions-in-views
You can't. HttpResponseRedirect sends a client-side redirect (HTTP status code 302) to the browser, and then the browser re-requests another page.
You can set a URL query string on the redirect, though that will be visible to the user and anyone intercepting HTTP requests (i.e. proxies), and is therefore not suitable for sensitive information.
The best way would probably be to use a coded querystring on the redirect URL... its an old school approach.
You could do something like
/page/?m=1, /page/?m=2, etc
You would then parse that variable with request.GET in the view code and show the appropriate message.
From your views.py you hast have to put a key/value-pair into the session and then read it from the HTML template.
For example:
views.py
# your code here
request.session['vote'] = 1
return HttpResponseRedirect(request.path)
your_template.html
{% ifequal request.session.vote 1 %}
<!-- Your action here -->
{% endifequal %}
The only way I know of to pass any data with a redirect is to add GET parameters to the URL you're passing in. To avoid XSS hacks you'd want to pass a specific constant like:
[current path you're passing in]?message=saved
And then process the message=saved parameter in the handler for the path you passed in.
A somewhat more complicated way would be not passing the data in the redirect, and instead using something like http://code.google.com/p/django-notify/ to store session-based data that is displayed to the user following the redirect.
You add ?saved=1 to the query string and check for it with something like:
saved = request.GET.get('saved', False)

Categories

Resources