I want to design my own HTML template with tags like JSP or Jade and then pass data from python to it and let it generate full html page.
I don't want to construct document at python side like with DOM. Only data goes to page and page tempalte decides, how data lays out.
I don't want to serve resulting pages with HTTP, only generate HTML files.
Is it possible?
UPDATE
I found Jinja2, but I has strange boilerplate requirements. For example, they want me to create environment with
env = Environment(
loader=PackageLoader('yourapplication', 'templates'),
autoescape=select_autoescape(['html', 'xml'])
)
while saying that package yourapplication not found. If I remove loader parameter, it complains on line
template = env.get_template('mytemplate.html')
saying
no loader for this environment specified
Can I just read template from disk and populate it with variables, without extra things?
Just use the FileSystemLoader:
import os
import glob
from jinja2 import Environment, FileSystemLoader
# Create the jinja2 environment.
current_directory = os.path.dirname(os.path.abspath(__file__))
env = Environment(loader=FileSystemLoader(current_directory))
# Find all files with the j2 extension in the current directory
templates = glob.glob('*.j2')
def render_template(filename):
return env.get_template(filename).render(
foo='Hello',
bar='World'
)
for f in templates:
rendered_string = render_template(f)
print(rendered_string)
example.j2:
<html>
<head></head>
<body>
<p><i>{{ foo }}</i></p>
<p><b>{{ bar }}</b></p>
</body>
</html>
Related
I am trying to create a very simple one-page Flask application for a python script that I have. The script requires multiple user inputs in a for-loop with the number of loops being user input as well.
Here is the code in my script to make it more clear:
def shared_books():
import requests as re
from bs4 import BeautifulSoup
import time
num_lists = int(input('Enter the number of lists you would like to search:'))
urls = []
page_counts = []
for i in range(num_lists):
urls.append(input(f'Enter the url for list {i + 1}:'))
page_counts.append(int(input(f'Enter the number of pages for list {i + 1}:')))
I want a simple HTML that will ask the user for the number of lists, then the URL and page count for each list as is shown in my function. Then it will run the entire function.
The HTML code I have right now is super simple and I don't want much else outside of the input parts:
<html>
<head>
<title>Goodreads-App</title>
</head>
<body>
<h1>Welcome to my app!</h1>
<<p>This app will allow you to see books that are
shared between multiple lists on goodreads</p>
</body>
</html>
Please let me know how I can set up this application!
Firstly, I suggest you take a look at the Flask docs. You are doing it right in terms of having a view function, but the input() python keyword doesn't work like that in Flask. Instead, you should render an html template which you can then put your form input field into. Here is an example:
from flask import Flask, render_template
#flask initialising stuff, read docs for info
#app.route("/home")
def home():
return render_template("home.html")
Flask runs on your computer's local server "localhost", which is not publicly accessible. It conventionally runs on port 5000, which gives the name "localhost:5000".
When someone visits "localhost:5000/home", flask will look for a file called "home.html" in a pre-designated templates folder – the default is a directory called "templates" which you should put your html files into.
So if this is your "home.html" file:
<html>
<head>
<title>Goodreads-App</title>
</head>
<body>
<h1>Welcome to my app!</h1>
<p>This app will allow you to see books that are
shared between multiple lists on goodreads</p>
</body>
</html>
When you load the page associated with a specific function, it will return a template which is rendered as html. The above should look something like this:
And that is how to start.
Thank you for the answers! I haven't quite solved the previous issue but have approached it from a different angle which is working now! I will potentially post again if I don't solve it.
I am using flask forms to do what I was trying.
I have Flask website in which I want to add download button which downloads .csv file with scraped data.
In my html file I have this code:
<a href="cms_scrape.csv" ><button>Download!</button></a>
And only output I get is error: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
File is in its proper folder.
My folder structure:
└───Project
│ cms_scrape.csv
│
└───templates
index.html
You will need to specify some sort of route on the backend of your site.
For instance, somewhere in your flask site, you probably have a route #app.route('/') for your index. You will need a similar route for your file. That route will go out onto your file system and return the file itself.
#app.route('/csv_file')
def csv_file():
return flask.send_file('path/to/file/cms_scrape.csv',
attachment_filename='cms_scrape.csv',
as_attachment=True)
You will also need to modify your html to access a route and not the file name directly (unless you create your routes dynamically, of course):
<a href="/csv_file" ><button>Download!</button></a>
Not exactly sure about this but I think the tag has a download attribute you can use. Then you don't need the button.
Usage:
<a href="/path/to/file" download>
Source: https://www.w3schools.com/tags/att_a_download.asp
You can make links to files with the
{{ url_for('static', filename='filename.foo') }}
function inside your template. You have to store the file in a folder named 'static' which should be located in the directory where the main scipt is.
The link in your template should look like this:
<a href=" {{ url_for('static', filename='cms_scrape.csv') }} " download>Download!</a>
I use two mechanisms for localization site:
1. I use the standard template tag {{ gettext 'some_text'}} in my index.html
2. I wrote custom jinja extension that takes the content of markdown file according to language that used on the site.
And I use Babel to create messages.pot file and then to create massages.po file.
I have this babel configuration in babel.cfg :
[jinja2: theme/templates/index.html]
silent = false
And this is my custom jinja extension - custom_jinja_extension.py :
from jinja2 import nodes
from jinja2.ext import Extension
from markdown import Markdown
class IncludeMarkdownExtension(Extension):
"""
a set of names that trigger the extension.
"""
tags = set(['include_markdown'])
def __init__(self, environment):
super(IncludeMarkdownExtension, self).__init__(environment)
def parse(self, parser):
tag = parser.stream.__next__()
ctx_ref = nodes.ContextReference()
if tag.value == 'include_markdown':
args = [ctx_ref, parser.parse_expression(), nodes.Const(tag.lineno)]
body = parser.parse_statements(['name:endinclude_markdown'], drop_needle=True)
callback = self.call_method('convert', args)
return nodes.CallBlock(callback, [], [], body).set_lineno(tag.lineno)
def convert(self, context, tagname, linenum, caller):
"""
Function for converting markdown to html
:param tagname: name of converting file
:return: converting html
"""
for item in context['extra_siteurls']:
if item == context['main_lang']:
input_file = open('content/{}/{}'.format('en', tagname))
else:
input_file = open('content/{}/{}'.format(context['main_lang'], tagname))
text = input_file.read()
html = Markdown().convert(text)
return html
I use this template tag - {% include_markdown 'slide3.md' %}{% endinclude_markdown %}
In my pelicanconf.py I add such strings for jinja extensions:
# Jinja2 extensions
JINJA_ENVIRONMENT = {
'extensions': [
'jinja2_markdown.MarkdownExtension',
'jinja2.ext.i18n',
'custom_jinja_extension.IncludeMarkdownExtension'
]
}
When I run the command:
pybabel extract --mapping babel.cfg --output messages.pot ./
I get this error
jinja2.exceptions.TemplateSyntaxError: Encountered unknown tag
'include_markdown'. Jinja was looking for the following tags:
'endblock'. The innermost block that needs to be closed is 'block'.
When I delete all using of custom template tag gettext work well. What I do wrong?
Trouble was in the path. Babel looking jinja extension in virtualenv in jinja folder, but my custom jinja extension was in the project folder.
That's why I run this command in the terminal
export PYTHONPATH=$PYTHONPATH:/local/path/to/the/project/
and change my babel.cfg :
[jinja2: theme/templates/index.html]
extensions=jinja2.ext.i18n, **custom_jinja_extension.IncludeMarkdownExtension**
silent = false
After this changes babel found my custom extension custom_jinja_extension and created messages.pot file correctly!
I'm working to modify a cookiecutter Flask app. I'm working locally on WIN7 .
I've set up bower to install the front end dependencies under the static root by using a .bowerrc file in the document root containing:
{ "directory" : "myflaskapp/static/bower_components" }
This cookiecutter uses flask-assets to manage the project assets. Following https://adambard.com/blog/fresh-flask-setup/ I've modified myflaskapp/assets.py file :
from flask_assets import Bundle, Environment
import os
css = Bundle(
"libs/bootstrap/dist/css/spacelab/bootstrap.css",
"bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.css",
"css/style.css",
"css/home.css",
filters="cssmin",
output="public/css/common.css"
)
js = Bundle(
"libs/jQuery/dist/jquery.js",
"libs/bootstrap/dist/js/bootstrap.js",
"bower_components/moment/moment.js",
"bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js",
"js/plugins.js",
filters='jsmin',
output="public/js/common.js"
)
assets = Environment()
assets.register("js_all", js)
assets.register("css_all", css)
The debug setting is set to false, meaning the assets should be compressed and minified.
Before I send a request:
After:
shouldn't the files go in static/public/css and static/public/js
This particular cookiecutter recipe has a public Blueprint that declares that its static files go into the static directory. I'm not sure why the author included empty static/public/{css,js} directories; they are probably just leftovers from an earlier stage of development and were neglected. I've removed the static/public directory in my instantiation of this recipe (well, a similar one with a similar problem) to no harm.
I am new to chameleon templates. i paste code snippet ..
runtemp.py
import os
path = os.path.dirname(__file__)
from chameleon import PageTemplateLoader
templates = PageTemplateLoader(os.path.join(path, "templates"))
template = templates['mytemp.pt']
template(name='John')
print str(template.read())
mytem.pt
<testtag>
<innertesttag>${name}</innertesttag>
</testtag>
But the output i got is
<testtag>
<innertesttag>${name}</innertesttag>
</testtag>
I was expectinng John in output instead od $(name)
What is going wrong ? how to render template?
template.read() just reads the contents of the template; you discarded the actual rendering result. template(name='John') returns the rendering.
Do this instead:
print template(name='John')