I have this function which gets data from an API and then uses Jinja2 to insert this data into an HTML file. I want this data to show up in my newsletter.html and be sent as an email. Everything works fine apart from
this happening:
Here are some relevant snippets:
Getting the data as list and templating with Jinja:
# movie_finder.py
movie_list = []
for item in now_playing['results']:
if genre_id in item['genre_ids']:
movie_list.append(item['original_title'])
print movie_list
# Create jinja2 environment
try:
env = Environment(loader=PackageLoader('movie_finder', 'templates'))
template = env.get_template('newsletter.html')
rend = template.render(info=movie_list)
print "Templating successful"
except:
print "Templating fail"
return "Templating fail"
find_movies_with_genre(API_KEY, 878)
In the script that sends the email (which otherwise works fine), I simply do:
from scifi_finder import find_movies_with_genre
Template:
<!-- Newsletter template by https://github.com/derekpunsalan/-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width"/>
<!-- For development, pass document through inliner -->
<link rel="stylesheet" href="css/simple.css">
<style type="text/css">
output print movie_list:
[u'Thor: Ragnarok', u'Blade Runner 2049', u'War for the Planet of the Apes', u'Geostorm']
You are not passing the variables correctly when calling render(), here is what you are doing:
template.render(info=movie_list)
You are passing a variable info, yet in your template, you are referring to movie_list:
{% for item in movie_list %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
This will not work, you need to refer to the variable you are passing, personally I prefer it this way:
template.render(movie_list=movie_list)
...and in template...
{% for item in movie_list %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
...as you have it already. You could just as well use...
{% for item in info %}
{{ item[0] }}
{{ item[1] }}
{% endfor %}
...in your template and keep your function call as it is, but personally I find the first variant less confusing when I read it again weeks later.
Related
I'm using a database to retrieve project numbers and put them in a search for a user in a form. I am using an api (ajax google api) for autocompletion on the search.
When putting it as autocomplete in search field it contains brackets and a comma.
I need it just to be like:
24403
24429
not like:
(24403,)
(24429,)
I am unsure how to do this. My current code is:
app.py
class Element(db.Model):
__tablename__ = 'elements'
ProjectNumber = db.Column(db.Integer, primary_key=True)
#app.route("/", methods=["POST", "GET"])
def basicTemplate():
project_number = Element.query.with_entities(Element.ProjectNumber).distinct()
if request.method == "GET":
return render_template('search.html', navigation=project_number)
if __name__ == '__main__':
app.run()
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/static\style.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<link href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/ui-lightness/jquery-ui.css" rel="stylesheet" type="text/css" />
{% block head %}
<title>{% block title %}{% endblock %}- base</title>
{% endblock %}
</head>
<body>
{% block body %}
{% block project_number %} {% endblock %}
{% endblock %}
</body>
</html>
search.html
{% extends 'base.html' %}
{% block head %}
{% endblock %}
{% block project_number %}
<label for="project_code">Project Number</label><input type="text" id="project_code" name="project_code">
<input type="submit">
<script>
$( function() {
var project = [
{% for test in navigation %}
"{{test}}",
{% endfor %}
];
$( "#project_code" ).autocomplete({
source: project
});
} );
</script>
{% endblock %}
The command with_entities always returns a list of tuples.
You can combine multiple jinja filters to get a list of strings that can be passed directly to javascript.
First you extract the first element of all tuples and convert them into strings. You convert the received generator into a list and output it in a javascript-compatible manner.
$(function() {
const projects = {{ navigation | map('first') | map('string') | list | tojson }};
$('#project_code').autocomplete({
source: projects
});
});
Of course you can also perform the extraction and conversion to the string in the endpoint.
Depending on how many projects are stored in your database, I recommend getting the data via ajax.
$(function() {
$('#project_code').autocomplete({
source: '/search',
});
});
#app.route('/search')
def search():
term = request.args.get('term', '')
projects = Element.query.filter(Element.ProjectNumber.ilike(f'%{term}%')).all()
return jsonify([str(p.ProjectNumber) for p in projects])
Using flask, I'm passing in a list of dictionaries to one of the pages. One of the variables contains html text (ex:var x = <h1>hello</h1>). How would I get it to display as hello rather than just print out "<h1>hello</h1>"? Here's my code so far (post.description has the html variable; It's equal to <h1>hello</h1>):
<!DOCTYPE html>
<html>
<head>
</head>
<body>
{% for post in posts %}
<p>{{post.title}}<p>
{{post.description}}
{% endfor %}
</body>
</html>
You can use safe to render the HTML code with Jinja.
Example: {{ post.description | safe }}
I'd like to visualize a form in home/admin page that allows users to select a value from a list (values belongs to a db table) and a button to execute a custom python method.
I am not able to understand if it's possibile to show a form without showing data of if it's possible to run code without flask-admin.
P.s. the same (simple) code that I use to create a form (just 2 datepickers) in Flask works but as soon as I put it in /home/admin the html and flask-admin cannot talk (exchange the values in the form) anymore.
Update:
This is part of my Flask-admin code:
class ExampleForm(Form):
dt_start = DateField('DatePicker', format='%Y-%m-%d')
dt_end = DateField('DatePicker', format='%Y-%m-%d')
#app.route('/admin', methods=['POST','GET'])
def index():
form = ExampleForm()
if form.validate_on_submit():
print("Start date: ", form.dt_start.data.strftime('%Y-%m-%d'))
print("End date: ", form.dt_end.data.strftime('%Y-%m-%d'))
return "Form is OK!"
return render_template('admin/index.html', form=form)
HTML CODE:
{% extends "admin/master.html" %}
{% block body %}
<head>
<title>Download form</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<div class="container">
<h1>Please insert the dates</h1>
<br>
<form action="#" method="post">
{{ form.dt_start(class='datepicker') }}
{{ form.hidden_tag() }}
{{ form.dt_end(class='datepicker') }}
{{ form.hidden_tag() }}
<input type="submit"/>
</form>
</div>
{% endblock %}
ERROR:
jinja2.exceptions.UndefinedError: 'form' is undefined
Thanks a lot
Alessandro
I've got a simple flask app, with a templates folder with a bunch of html files that are created by a separate program. I want to (1) serve each of these html files by hitting localhost:8888/<html_filename> and
(2) create a directory with hyperlinks to these endpoints on my main / endpoint.
Thoughts on how I could get a jinja template to create links to those endpoints? Heres what I've been thinking.
Flask App:
#app.route('/')
def index():
reports = [f_name for f_name in os.listdir("templates") if f_name.endswith(".html")]
return render_template("index.html", reports=reports)
#app.route('/<report>')
def render_report(report):
return render_template(report+'.html')
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report Directory</title>
</head>
<body>
<ul>
{% for r in reports %}
<li>
{{ r }}
</li>
{% endfor %}
</ul>
</body>
</html>
Off the top of my head and not tested in any way define a route along the lines of the following:
#route("/<string:slug>/", methods=['GET'])
def page(self, slug):
if slug_exists_as_a_html_file(slug):
return render_template(slug)
abort(404)
The function (or inline it) )slug_exists_as_a_html_file needs to return True if the slug matches a valid html template file, otherwise false.
To generate your report listing use something like :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report Directory</title>
</head>
<body>
<ul>
{% for r in reports %}
<li>
{{ r }}
</li>
{% endfor %}
</ul>
</body>
</html>
Since my last question here: Python images display
I understood that from all the answers I got the glob.glob could be the only one in the direction I need.
However where I am stuck right now is here:
I can create a list with all the filenames in my media directory by using glob.glob:
all = glob.glob("/Path_to_MEDIA/*/*.jpg")
But how can I use that and create a VERY SIMPLE image display with one next button that calls files in my MEDIA_ROOT and displays them.
What I know is:
I have a Template which looks something like the default directory index:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-us" />
<meta name="robots" content="NONE,NOARCHIVE" />
<title>Index of {{ directory }}</title>
</head>
<body>
<h1>Index of {{ directory }}</h1>
<ul>
{% ifnotequal directory "/" %}
<li>../</li>
{% endifnotequal %}
{% for f in file_list %}
<li>{{ f }}</li>
{% endfor %}
</ul>
</body>
</html>
I need to create a def in my views that feeds the list from glob.glob to this or similar template.
What I dont know:
How does this def in the view have to look like?
And here:
What do I have to write to display one image, sound in a browser?
What do I have to write to display a LIST of images, sounds?
Thanks for the time!
Make a direct-to-template url with extra-context in urls.py:
from django.views.generic.simple import direct_to_template
...
url(r'^whatever', direct_to_template,
{ 'template':'foo.html', 'extra_context': {'files':myfiles} }
name='whatever' ),
Where myfiles above is a list/tuple of your files. However, make sure to format your file list in terms of MEDIA_URL instead of based on MEDIA_PATH. For example:
myfiles = [ 'relative/path/foo.jpg',
'http://static.mysite.com/absolute/path/bar.jpg' ]
Though, obviously generated from the filesystem in your case, not a hardcoded list. And you could do the work in a view rather than using direct-to-template -- just make sure to put the files key/value into your context:
def myview( request ... ):
context = RequestContext(request)
context[files]=myfiles
return render_to_respone( ..., context_instance=context )
Then, in your template foo.html:
{% for file in files %}
<img src='YOUR_MEDIA_URL_HERE/{{ file }}' />
{% endfor %}