Viet Phan X

Building MVP with Flask Day 7 – Left Join with SQLAlchemy

👨‍💻 Software Development
2331
Apr 14, 2019

Hi everyone.

Yesterday, I managed to fill the SQLite database with some dummy data and display it using Jinja2 template tags. Today, I would like to polish my design a little bit more. My idea is to use Bootstrap 4 cards design to display an ordered list of products. Why not use table layout you ask?

Well, because Bootstrap 4 tables are great for a comprehensive list of items, where each item is represented by plain text. Plain text is boring. I want images, badges, and alerts grouped into a single cell. With Bootstrap 4 cards, I can do that with a built-in grid system using columns and rows. I will lose responsiveness thought.

Bootstrap 4 cards are not responsive and resizing your screen will break the layout. My approach is to create a separate mobile web version, that will sit on a subdomain. Whenever a user visits my web from mobile, Cloudflare will redirect him to my mobile version.

It sounds more complicated than just bending my design with CSS media queries. We will see. I will make a decision when I see significant traffic coming from mobile phones.

Okay. Back to my design. Let's add some HTML.

List 1 2019 03 06 17 44 43 1024x319

Cool. Each Bootstrap 4 card displays one product and its name.

How to do left join with SQLAlchemy

To add the company name to each card, I need to figure out how to do left join in Flask-SQLAlchemy. I designed my database so company data are stored in a separate Company Table. If I left join Company table to the Products table, I will be able to get a dataset I want.

Will this ORM query work?

products = Product.join(Company, Company.id == Product.company_id, isouter=True)

Nope. Got Internal server error.

500 Internal Server Error 2019 02 27 17 01 13 3

Gunicorn logs reveal why.

AttributeError: type object 'Product' has no attribute ‘join'

Ok, seems I forgot query syntax in the query.

products = Product.query.join(Company, Company.id == Product.company_id, isouter=True)

Nice, no error this time. Tables are joined.

ORM query above will translate into plain SQL like this.

SELECT product.id AS product_id, product.name AS product_name, product.company_id AS product_company_id
FROM product LEFT OUTER JOIN company ON company.id = product.company_id

But where is the column from the left joined Company table?

I try to print query results set into the command line to see what's happening.

for row in products:
    print(row.Product)
    print(row.Company)

The refreshing Flask page ends with an error.

print(row.Product)
AttributeError: 'Product' object has no attribute 'Product'

By looking at SQLAlchemy documentation I overhaul query syntax.

products = db.session.query(Product, Company).join(Company, Company.id == Product.company_id).all()

Query result set above will finally return both tables and their columns.

[(<Product 1>, <Company 1>), (<Product 2>, <Company 2>)]

Now I should be able to access the company name column in the template.

List 2 2019 03 06 18 22 59

Great.

Importing custom CSS

The product title is displayed together with the company name, but it is ugly as f. Let's polish design a little more with our CSS styles.

To do that, I have to create a new folder called "static" inside my project folder and create a new CSS file style.css. Then I need to include style.ccs into the header section.

<link rel= "stylesheet" type= "text/css" href= "{{ url_for('static',filename='styles/style.css') }}">

When I try to load the CSS file in my browser, I get not found error.

404 Not Found 2019 03 06 18 36 00 1024x333

Instead of a dynamic URL, I try to add the absolute path to my CSS file.

http://financia.local/static/css/styles.css

No luck. I try to declare a static path in __init__ file like below.

app = Flask(__name__, static_url_path='/static’)

Still not found error. This is driving me nuts and my time is running out. I will give it a shot next time.

Useful Jinja2 template functions

Today, I want to finish my layout with a few template tricks. Like, how to round float numbers with Jinja2 syntax.

product["price"]|round(1)

Or how to convert float number to integer number.

{{product["price"]|round(0)|int}}

And what if I don't want trailing zeroes behind my float numbers? I will use rstrip.

{{product["float"].rstrip('0').rstrip('.')}}

Ok, time to call it a day. I didn't progress much today.

In the next session, I want to focus on solving the puzzle with static files. Without it, I won't be able to serve images, javascript codes, and CSS files.

Thanks for reading and see you next time!


You need a

Innovation Advisor

Fresh eyes help generate new perspectives. Book a free call in which we identify hidden opportunities.

15 min call

Get product feedback


If You Like what I do
You can buy me a coffee 👇
Send via PayPalSend with Bitcoin

🤙 Let's connect

Made by Viet Phan © 2018-2024Privacy Policy