Building MVP with Flask Day 8 β Serving Static Files
Hi everyone, Today, I want to finally solve my issue with serving static files with the Flask framework.
Static files in Django / Flask world are all files hosted on your server, that does not contain any python code. Which are all non .py files, likes CSS, JS, images, etc.
I don't know why is Jinja2 template system built this way, but linking images or other static files in your HTML template won't make them magically appear in your browser. Your browser will complain, that files cannot be found.
With Apache stack (PHP, HTML), you would just point to your static files path, like www.domain.com/images/image.png. Url is then translated into an absolute path on your server, like /www/images/image.png.
But with Nginx and Gunicorn, nothing works out of the box. I have to make some configurations before I can display my static files.
Let's do it now.
Configure Nginx
Look at the Nginx configuration if the path to static files is properly configured.
usr/local/etc/nginx/nginx.conf
It seems the path is wrong.
location /static {
root /path/fiancia/static/; # <- let nginx serves the static contents
}
I will change it to...
location /static {
root /path/financia/; # <- let nginx serves the static contents
}
Otherwise, I would have to store my static files at path/financia/static/static/images/image.png.
This was the most frustrating thing I faced as a beginner. I didn't know that Nginx adds additional /static/ to your path. I lost a few days of trials and errors before I figured out.
Ok, now I restart Nginx so the changes in configuration are applied.
sudo brew services restart nginx
If I try to load http://financia.local/static/css/style.css in browser, it works.
Add Font-awesome
Now I want to have nice icons, so I will add Font-awesome icons to Flask.
pipenv install Flask-FontAwesome
Then initialize Font-awesome in our __init__.py file.
from flask_fontawesome import FontAwesome
fa = FontAwesome(app)
Finally, load Font-awesome static files into HTML, most likely into my base HTML template.
{{ fontawesome_html() }}
Hmm. Fonts are not loaded.
I check generated HTML code with Chrome developer console.
Links to Font-awesome static files have been generated, but when I try to load them, files are not found.
<link href="/static/fontawesome/css/fontawesome.min.css?version=5.3.1" rel="stylesheet”>
<link href="/static/fontawesome/css/solid.min.css?version=5.3.1" rel="stylesheet”>
Right, I have to manually download those files from the official Font-awesome website and copy-paste files into the path mentioned above.
Now it works.
Toggle Tooltip Font-awesome icon with jQuery
Next, I want to use the Font-awesome icon as a tooltip. You hover your cursor over the icon and a text container appears above icon.
Because I user plain Bootstrap 4 HTML elements without any javascript, I need to use JQuery to make tooltips dynamic.
<script type="text/javascript">
$(function () {
$("[rel='tooltip']").tooltip();
});
</script>
This should take care of the hovering thing.
But it is not.
What about pointing JQuery to data-toggle parameter as demonstrated in Bootstrap 4 documentation.
<script type="text/javascript">
$(function () {
d$('[data-toggle="tooltip"]').tooltip()
});
</script>
Now it works.
Display static image in Jinja2
Next, I want to display images in Jinja2 templates. There is a Jinja2 tag for that.
<img src="{{ url_for('static', filename=/static/logo.jpg) }}" class="rounded" alt="company">
Damn, no image found.
Img tag above somehow generated a weird image source path.
<img src="/static//static/images/logo.jpg" class="rounded" alt="company”>
Okay, let's remove /static/ from the image path.
Now, the image is displayed, but it does not fit the parent div element.
Bootstrap 4 documentation says I should apply img-fluid CSS class, which fixed the issue.
I am exhausted, but I would like to do one last thing.
Left join product with company data
Left join another table and display left joined data in the Jinja2 template.
First, I need to edit the ORM query.
products = db.session.query(Product, Company, ProductType).join(Company, Company.id == Product.company_id).join(ProductType, ProductType.id == Product.product_type_id).all()
The refreshing web page throws internal server error.
Gunicorn logs reveal the root cause.
{% for product, company in products %}
ValueError: too many values to unpack (expected 2)
I forgot to edit my Jinja2 template loop tag with the left joined table.
{% for product, company, product_type in products %}
Great.
Today was a productive day. Querying and displaying data works. Loading and displaying static files works too.
In the future, I will continue my development in silence if you don't mind. Now it is only about designing webpage with Bootstrap 4 components, which is boring. But if I face an interesting roadblock, I promise I will write about it, so you can learn from my mistakes.
Thanks for reading and see ya next time.