I am a Newbee in Python, Flask and API, and trying to learn it with my own project.
The API I am querying requires Basic Authentication.
Created a login.html and dashboard.html as templates of Flask.
Created a module myclasses.py
and the reporter.py which is the main module for Flask Views and other code.
login.html request user for IP, Username and Password which is captured in (/) view, and then forwarded to the Function defined in MyClasses.py using "call_api" to form the API and the function returns the Data.
Now I don't know and not sure how to proceed with forwarding the received data as json to (/dashboard) view for parsing and displaying in Dashboard template page.
from flask import Flask, render_template, url_for, redirect, request, json
from MyClasses import call_api
app = Flask(__name__)
data = "no data"
status_code = 0
#app.route('/dashboard')
def dashboard():
return render_template('dashboard.html', data=data, status_code=status_code)
#app.route('/', methods=["GET", "POST"])
def login():
if request.method == "POST":
creds = {'ipaddr': request.form['inip'],
'username': request.form['inusername'],
'password': request.form['inpassword'],
'entity': 'info'
}
request_dump = call_api(creds['ipaddr'], creds['username'], creds['password'], creds['entity'])
if request_dump[1] == 200:
global data
global status_code
data = (json.dumps(request_dump[0], indent=2))
status_code = request_dump[1]
return redirect(url_for('dashboard')), status_code, data
else:
return render_template('login.html')
else:
return render_template('login.html')
I am getting error, what does this means?
ValueError: not enough values to unpack (expected 2, got 1)
If I remove status_code, data, it works fine.
return redirect(url_for('dashboard')), status_code, data
for sure, I am not doing it the right way in many areas of this code.
Also, If you guys tell me on how to debug the code when flask is involved, I tried using breakpoints in PyCharm but code does not stops when I browse the templates.
Appreciate you help and Thank you for the time.
reconstructing the dictionary to a customized (data that I need) within the dashboard() as well as correcting the Flask template with {% for loop %} resolved the issue.
Flask Template snippet below:
<table class="containertbl">
<tbody>
{% for key,value in ddo.items() %}
<tr>
<th scope="row">{{ key }}</th>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
Related
I am learning Flask. I wrote the basic code and I want the submitted text to display in the same page. I already wrote the html and connected it. How can I do this?
from flask import Flask, redirect, url_for,render_template, request
app = Flask(name)
#app.route("/", methods=["POST", "GET"])
def home():
if request.method == "POST":
user = request.form["nm"]
return redirect(url_for("/", user))
else:
return render_template("login.html")
if name == ("main"):
app.run(debug=True)
I've noticed that you've taken the code from Python Basics. Indeed they do not show how to format the HTML template of the redirect.
Luckily, they offer a tutorial that shows you how to feed retrieved data to an HTML template using Jinja2. This tutorial can be found here. In essence, you can use {{ variable }} in your HTML template. In Flask, you will have to specify the variable as argument in the render_template function.
Minimal example:
# app.py
#app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
variable = request.form['variable']
return render_template("result.html", variable=variable)
<!-- result.html -->
<p> This is your variable: {{ variable }} </p>
I advice you to also check out both the Flask and Jinja2 documentation, as they offer plenty comprehensive examples of how to work with callbacks and HTML templating.
I'm trying to make a todo list web app with Flask. I need to make an instance of a database to store the tasks. For some reason when I try to make the instance it doesn't work. I am sure this is the issue because when I remove the part that uses the database from the code it runs fine.
Here is the code
from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
flask1 = Flask(__name__)
# I think this is telling our app where to look for the database
# Three slashes == relative path. four == absolute path
flask1.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
# initializing the database
db = SQLAlchemy(flask1)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(200), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.ctime)
def __repr__(self):
return "<Task %r>" % self.id
# To actually instatiate the database
# 1- start python shell
# 2- import db
# 3- db.create_all()
# 4- exit shell
#flask1.route('/', methods=['POST', 'GET'])
def index():
if request.method == 'POST':
task_content = request.form['content']
new_task = Todo(content=task_content)
try:
db.session.add(new_task)
db.session.commit()
return redirect('/')
except:
return 'there was an issue adding the task'
else:
tasks = Todo.query.order_by(Todo.date_created).first
return render_template('index.html', tasks=tasks)
if __name__ == "__main__":
flask1.run(debug=True)
Now in the else block, if I return render_template like this
return render_template('index.html')
There is no error. This is because I use the tasks variable in my index.html file
Here is the code HTML code that generates the error
<!-- {% for task in tasks %} -->
<tr>
<td>{{ task.content }}</td>
<td> {{task.date_created.date }}</td>
<td>
delete
<br>
Update
</td>
</tr>
<!-- {% endfor %} -->
The error I'm getting right now is TypeError: 'NoneType' object is not iterable. I believe this means that the instance of my database was not successfully created.
I try to create the db instance in the following method
1- open python shell
2- import this script
3- db.create_all()
4- exit python shell
Any help is appreciated
Sorry for a long question
I think you meant to get all the tasks and call all():
tasks = Todo.query.order_by(Todo.date_created).all()
Be forewarned of a triple-newbie threat - new to python, new to python anywhere, new to flask.
[pythonanywhere-root]/mysite/test01.py
# A very simple Flask Hello World app for you to get started with...
from flask import Flask
from flask import render_template # for templating
#from flask import request # for handling requests eg form post, etc
app = Flask(__name__)
app.debug = True #bshark: turn on debugging, hopefully?
#app.route('/')
#def hello_world():
# return 'Hello from Flask! wheee!!'
def buildOrg():
orgname = 'ACME Inc'
return render_template('index.html', orgname)
And then in [pythonanywhere-root]/templates/index.html
<!doctype html>
<head><title>Test01 App</title></head>
<body>
{% if orgname %}
<h1>Welcome to {{ orgname }} Projects!</h1>
{% else %}
<p>Aw, the orgname wasn't passed in successfully :-(</p>
{% endif %}
</body>
</html>
When I hit up the site, I get 'Unhandled Exception' :-(
How do I get the debugger to at least spit out where I should start looking for the problem?
The problem is render_template only expects one positional argument, and rest of the arguments are passed as keyword only arguments.So, you need to change your code to:
def buildOrg():
orgname = 'ACME Inc'
return render_template('index.html', name=orgname)
For the first part, you can find the error logs under the Web tab on pythonanywhere.com.
You need to also pass your name of orgname variable that is used in your template to render_template.
flask.render_template:
flask.render_template(template_name_or_list, **context)
Renders a template from the template folder with the given context.
Parameters:
template_name_or_list – the name of the template to be rendered,
or an iterable with template names the first one existing will be rendered
context – the variables that should be available in the context of the template.
So, change this line:
return render_template('index.html', orgname)
To:
return render_template('index.html', orgname=orgname)
After reading many similar sounding problems and the relevant Flask docs, I cannot seem to figure out what is generating the following error upon submitting a form:
400 Bad Request
The browser (or proxy) sent a request that this server could not understand.
While the form always displays properly, the bad request happens when I submit an HTML form that ties to either of these functions:
#app.route('/app/business', methods=['GET', 'POST'])
def apply_business():
if request.method == 'POST':
new_account = Business(name=request.form['name_field'], email=request.form['email_field'], account_type="business",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="business")
#app.route('/app/student', methods=['GET', 'POST'])
def apply_student():
if request.method == 'POST':
new_account = Student(name=request.form['name_field'], email=request.form['email_field'], account_type="student",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'], q8=request.form['q8_field'],
q9=request.form['q9_field'], q10=request.form['q10_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="student")
The relevant part of HTML is
<html>
<head>
<title>apply</title>
</head>
<body>
{% if accounttype=="business" %}
<form action="{{ url_for('apply_business') }}" method=post class="application_form">
{% elif accounttype=="student" %}
<form action="{{ url_for('apply_student') }}" method=post class="application_form">
{% endif %}
<p>Full Name:</p>
<input name="name_field" placeholder="First and Last">
<p>Email Address:</p>
<input name="email_field" placeholder="your#email.com">
...
The problem for most people was not calling GET or POST, but I am doing just that in both functions, and I double checked to make sure I imported everything necessary, such as from flask import request. I also queried the database and confirmed that the additions from the form weren't added.
In the Flask app, I was requesting form fields that were labeled slightly different in the HTML form. Keeping the names consistent is a must. More can be read at this question Form sending error, Flask
The solution was simple and uncovered in the comments. As addressed in this question, Form sending error, Flask, and pointed out by Sean Vieira,
...the issue is that Flask raises an HTTP error when it fails to find a
key in the args and form dictionaries. What Flask assumes by default
is that if you are asking for a particular key and it's not there then
something got left out of the request and the entire request is
invalid.
In other words, if only one form element that you request in Python cannot be found in HTML, then the POST request is not valid and the error appears, in my case without any irregularities in the traceback. For me, it was a lack of consistency with spelling: in the HTML, I labeled various form inputs
<input name="question1_field" placeholder="question one">
while in Python, when there was a POST called, I grab a nonexistent form with
request.form['question1']
whereas, to be consistent with my HTML form names, it needed to be
request.form['question1_field']
I'm trying to use flask.g to store variables that can be accessed in other functions, but I don't seem to be doing something correctly. The application generates the following error when I try to access g.name: AttributeError: '_RequestGlobals' object has no attribute 'name'.
The documentation for flask.g says:
Just store on this whatever you want. For example a database
connection or the user that is currently logged in.
Here's a complete, minimal example that illustrates the error that I receive when trying to access the variable outside of the function it was created in. Any help would be greatly appreciated.
#!/usr/bin/env python
from flask import Flask, render_template_string, request, redirect, url_for, g
from wtforms import Form, TextField
application = app = Flask('wsgi')
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
g.name = name
# Need to create an instance of a class and access that in another route
#g.api = CustomApi(name)
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
# Need to access the instance of CustomApi here
#api = g.api
name = g.name
return render_template_string(name_template, name=name)
class LoginForm(Form):
name = TextField('Name')
template_form = """
{% block content %}
<h1>Enter your name</h1>
<form method="POST" action="/">
<div>{{ form.name.label }} {{ form.name() }}</div><br>
<button type="submit" class="btn">Submit</button>
</form>
{% endblock %}
"""
name_template = """
{% block content %}
<div>"Hello {{ name }}"</div><br>
{% endblock %}
"""
if __name__ == '__main__':
app.run(debug=True)
The g object is a request-based object and does not persist between requests, i.e. g is recreated between your request to index and your request to get_posts.
Application Globals in Flask:
Flask provides you with a special object that ensures it is only valid for the active request and that will return different values for each request. In a nutshell: it does the right thing, like it does for request and session.
For persistent storage of tiny data between requests use sessions instead. You may (but should not) get away with storing the data in the app object directly for global (all sessions) application state, similar to what config does, if you find a really good reason to do so.
For more complex data use databases.
If you need to track authentication information, I'd suggest one of the Flask plugins like Flask-Login or Flask-Principal.
For example, we use Flask-Principal. It raises the identity-loaded signal when somebody authenticates (or it detects an authentication cookie). We then map their logged-in identity with a user in our database. Something like this:
# not actual code
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
user = Person.query.filter(Person.username==identity.person.username).one()
g.user = user
and then we can use g.user in any controller or template. (We're actually ripping a lot of this out, it was a easy, lazy hack that's caused more trouble than it's worth.)
If you don't want to use a module, there's a built-in signal you can hook into at the start of every request:
http://flask.pocoo.org/docs/tutorial/dbcon/
# This runs before every request
#app.before_request
def before_request():
g.user = your_magic_user_function()
and g.user would then be magically available everywhere.
I hope that helps!
Just use sessions in flask. In your case, you just want to save the user/name in your request and the easiest way is to use sessions.
from flask import session
app.secret_key = 'some key for session'
Then, your functions could be changed as below:
#app.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
session['name'] = form.name.data
return redirect(url_for('get_posts'))
else:
return render_template_string(template_form, form=form)
#app.route('/posts', methods=['GET'])
def get_posts():
if 'name' in session:
name = session['name']
else:
name = "Unknown"
return render_template_string(name_template, name=name)
I will like to shed more light on the use of g global in storing data. g only store data with a request and when redirecting to another route, the g global is set back to null i.e it reset back to nothing. This means whatever set to g in one request can't be access in another request. Use sessions to store data that will be accessed across request.
One benefit of using g global is when connecting to a database to fetct a user. For example, may be the admin from the database. The admin can be store in the g global using the below method.
from flask import Flask, g
app = Flask(__name__)
#app.before_request
def text():
g.a = User.query.filter_by(email='admin#gmail.com')
#app.route("/getTrue", methods=['GET', 'POST'])
def getTrue():
form = UserForm()
if form.validate_on_submit():
if g.a == form.email.data:
return "Admin is logged in"
else:
return "User is logged in"
return render_template('login.html', form=form)
In the example above, the g can be use to save data which will be use in another request. I hope this help. Thanks