04 - Modeling the Models
In this module we will define our database models and schemas
Preparation Steps
Flask-SQLAlchemy provides a nice, easy to use, high level, ORM (object relational mapper). ORM's allow us to easily manipulate our database, with tiny bits of code. To do this, we use something called migrations
. More on these later.
We are also going to add Bcrypt
. Bcrypt is a nice encryption library that will help us with hashing and verifying passwords
Open up the file models/__init__.py
and add the following code. When we add to an __init__
, the entire package (directory) will be able to access from here

This will allow us to bind our models to SQLAlchemy's Model
and Column
objects.
The User Model
Open up models/user.py
and add the following code
import datetime
from marshmallow import fields, Schema # we will use these later
from . import db
from ..app import bcrypt
from .blog_post import BlogPostSchema
class UserModel(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(128), unique=True, nullable=False)
password = db.Column(db.String(128), nullable=False)
created_at = db.Column(db.DateTime)
modified_at = db.Column(db.DateTime)
# navigational property
blogposts = db.relationship('BlogPostModel', backref='users', lazy=True)
def __init__(self, data):
self.name = data.get('name')
self.email = data.get('email')
self.password = self._generate_hash(data.get('password'))
self.created_at = datetime.datetime.utcnow()
self.modified_at = datetime.datetime.utcnow()
def __repr__(self):
return f'<id {self.id}>'
def _generate_hash(self, password):
return bcrypt.generate_password_hash(password, rounds=10).decode('utf-8')
def check_hash(self, password):
return bcrypt.check_password_hash(self.password, password)
def delete(self):
'''deletes row from db'''
db.session.delete(self)
db.session.commit()
def save(self):
'''saves current state of model to db'''
db.session.add(self)
db.session.commit()
def update(self, data):
'''takes in data to modify model'''
for key, item in data.items():
if key == 'password':
self.password = self._generate_hash(value)
setattr(self, key, item)
self.modified_at = datetime.datetime.utcnow()
db.session.commit()
@staticmethod
def get_all_users():
return UserModel.query.all()
@staticmethod
def get_user_by_email(value):
return UserModel.query.filter_by(email=value).first()
@staticmethod
def get_one_user(id):
return UserModel.query.get(id)
This is a lot to understand what's going on all at once. So, let's break it down step by step.
__tablename__
: This variable will tell sqlalchemy what to call the table when it migrates
line 10 - 15 : These class attributes define what will go into the table and how the columns will be represented
__init__
: This is just a constructor, it will handle creating our objects before we persist data to the database
save/update/delete
: These methods perform the equivalent actions to an entry to the database. Notice that in the update
method, we never explicitly replace the actual instance attributes. This is still fine, because SQLAlchemy will ONLY commit attributes that are listed as db.Column
get_one_user/get_all_users
: These methods are self explanatory however they are static methods. Think of them as not being tied to an instance and have an overarching view on the database
repr
: Just in case we want to stringify the instances
generate_hash/check_hash
: We also have a couple of helper methods that incorporate bcrypt. These are mostly just abstraction steps so we don't need the dependency throughout the entire project.
blogposts
: This is navigational property. This just tells SQLAlchemy that there is a connection between these two models, but we haven't built out the blog post model yet. This is called coding with intent. We intend to implement it, it's just not there yet. Let's jump into it now
The Blog Post Model
Add the following to models/blog_post.py
from . import db
from datetime import datetime
class BlogPostModel(db.Model):
__tablename__ = 'blogposts'
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
title = db.Column(db.String(128), nullable=False)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime)
modified_at = db.Column(db.DateTime)
def __init__(self, data):
self.owner_id = data.get('owner_id')
self.title = data.get('title')
self.content = data.get('content')
self.created_at = datetime.utcnow()
self.modified_at = datetime.utcnow()
def __repr__(self):
return f'<id {self.id}>'
def delete(self):
db.session.delete(self)
db.session.commit()
def save(self):
db.session.add(self)
db.session.commit()
def update(self, data):
for key, item in data.items():
setattr(self, key, item)
self.modified_at = datetime.utcnow()
db.session.commit()
@staticmethod
def get_all_blogposts():
return BlogPostModel.query.all()
@staticmethod
def get_one_blogpost(id):
return BlogPostModel.query.get(id)
Take note of the owner_id
on line 10 in the above snippet. This is our foreign key that links us to the user
model, specifically the users primary key. This will aid us when we start creating our routes that are responsible for deleting and editing the blog posts. Essentially, we don't want other users to edit, or even delete, someone else's post
Last updated
Was this helpful?