Skip to content

Instantly share code, notes, and snippets.

@jpanganiban
Last active August 29, 2015 14:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpanganiban/c507f4091600fef5816c to your computer and use it in GitHub Desktop.
Save jpanganiban/c507f4091600fef5816c to your computer and use it in GitHub Desktop.
MVC pattern.
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/<user_id>/posts')
def api_posts(user_id):
"""Displays a page that returns all the posts created by this user."""
# !IMPORTANT
ctx = {}
# `first_or_404` is a useful shortcut to:
# user = User.query.filter_by(id=user_id).first()
# if not user:
# abort(404)
user = User.query.filter_by(id=user_id).first_or_404()
ctx['user'] = user
ctx['posts'] = user.posts
return render_template('user_posts.html', **ctx)
"""
In this example, I won't be using the `db.relationship` function but instead
implement the entry associations manually by hand.
The obvious advantage to this route (vs using the `db.relationship` function)
is the explicitness (explicit over implicit). Another is lazy-loading (multiple
SELECT queries vs a single JOINED query). This can be done with the `db.relationship`
function by adding a `lazy=True`.
If you look deep in the `db.relationship` function implementation, setting the
`lazy` option implements it in this same exact way (lazy functions calling another
SELECT query). It can also be configured to do an eager load as well.
The `db.relationship` function has one big advantage though: Declaration on what to
do with the orphan objects (ie. deleting the objects if the parent is deleted).
See http://pythonhosted.org/Flask-SQLAlchemy/models.html#one-to-many-relationships
"""
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255), unique=True)
# Manually implement associations. Use of these attributes will
# `lazy-load` the value. Effectively making another query as
# opposed to having a single query with a join (eagerly-loaded).
# Here's a link about it: https://en.wikipedia.org/wiki/Lazy_loading.
#
# The `@property` makes this function behave like an attribute but
# its value is lazy-loaded.
@property
def posts(self):
"""Returns all the posts this user created."""
return Post.query.filter_by(user_id=self.id).all()
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, index=True, unique=False)
content = db.Column(db.Text)
# Similar to the example above but implements the forward association.
# And this is also lazily-loaded.
@property
def user(self):
"""Returns the user that owns this post."""
return User.query.filter_by(id=self.user_id).first()
<!--
Here's a new pattern I've been experimenting with. I got this idea from
ReactJS components concept. This one however, implemented with Flask macros.
Read about web components:
http://webcomponents.org/presentations/complementarity-of-react-and-web-components-at-reactjs-conf/
-->
{% macro PostInfo(post) %}
<span class="author">{{ post.user.username }}</span>
<span class="publication-date">{{ post.created_at }}</span>
{% endmacro %}
{% macro Post(post) %}
<div class="info">
{{ PostInfo(post) }}
</div>
<div class="content">
{{ post.content }}
</div>
{% endmacro %}
<!doctype html>
<html>
<body>
<div id="posts">
{% for post in posts %}
<div class="post">
{{ Post(post) }}
</div>
{% endfor %}
</div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment