Databases and SQL
Databases are used to store data. SQLite is a simple database engine which uses SQL for queries. SQL (Structured Query Language) is a language used to interface with databases. A database consists of tables, which consist of columns called fields and rows called records. Each column has a fixed data type and each row is a single entry into the database. For example, users could be a table, username could be a field with type text and each user would have a record.
Data types
There are 4 data types in SQLite: text, integer, real and blob (binary data).
Basic operations
Create table:
CREATE TABLE users(
id INTEGER PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
Note: A column with type INTEGER PRIMARY KEY will auto-increment.
Delete table:
DROP TABLE users;
Insert record:
INSERT INTO users (username, password) VALUES ('bob', '513e35...0e7252');
Select record:
SELECT username, password FROM users WHERE id = 1;
Update record:
UPDATE users SET username = 'steve' WHERE id = 1;
Delete record:
DELETE FROM users WHERE id = 1;
Additional parts
ORDER BY column - Ascending order the results by column.
ORDER BY column DESC - Descending order the results by column.
LIMIT n - Give the first $n$ results.
Relationships
It is possible to create relationships between tables using foreign keys. This is a field in one table which refers to the primary key in another.
SQLAlchemy
SQLAlchemy is a Python ORM (Object-Relational Mapping) for working with
databases as objects in Python instead of raw SQL. There is a wrapper
for Flask, flask-sqlalchemy, which can be used to work with databases
in Flask applications.
Integrating into a Flask app
To add a database to an app, do the following:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///dbname.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db = SQLAlchemy(app)
The app is now linked to the database but needs models to work with it.
Executing raw SQL
It is still possible to execute raw SQL queries through SQLAlchemy using
db.session.execute.
from sqlalchemy import text
db.session.execute(sqlalchemy.text("SELECT * FROM users WHERE id = :val"), {"val": 1})
Database operations
Using the same examples as for raw SQL, the models file would be
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.Text, nullable = False, unique = True)
password = db.Column(db.Text, nullable = False)
This can then be created by running db.create_all().
A record can be inserted by creating an instance of the model class,
then using db.session.add:
new_user = User(username = "bob", password = "513e35...0e7252")
db.session.add(new_user)
db.session.commit()
For changes to be saved, you need to commit them using
db.session.commit().
There are two ways to get a record from the database. The first is using
db.session.query:
db.session.query(User).get(1). This gets the User object with id 1.
The other, simpler method is using User.query.get(1) which returns the
same thing.
To get records by something other than id you can use filter_by.
User.query.filter_by(name = "bob") is equivalent to
SELECT * FROM users WHERE name = ’bob’. This will return a list of
results even if there is only one result or None if there are none.
User.query.filter_by(name = "bob").first() will return the first
record that matches and User.query.filter_by(name = "bob").all() will
return all matching records.
To get all records in a table use User.query.all()
A record can be updated by getting it, then modifying it's attributes and commiting.
user = User.query.get(1)
user.name = "steve"
db.session.commit()
A record can be deleted by getting it and using db.session.delete.
user = User.query.get(1)
db.session.delete(user)
db.session.commit()
Relationships
Suppose we have two tables User and Post as follows:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.Text, nullable = False, unique = True)
password = db.Column(db.Text, nullable = False)
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.Text, nullable = False)
content = db.Column(db.Text, nullable = False)
To create a relationship between these tables we can use db.ForeignKey
and db.relationship.
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key = True)
username = db.Column(db.Text, nullable = False, unique = True)
password = db.Column(db.Text, nullable = False)
posts = db.relationship("post", backref = "author", lazy = "dynamic")
class Post(db.Model):
id = db.Column(db.Integer, primary_key = True)
title = db.Column(db.Text, nullable = False)
content = db.Column(db.Text, nullable = False)
author_id = db.Column(db.Integer, db.ForeignKey("user.id"))
Now Post has a column containing the author's id which is marked as a
foreign key which is the id field of the user table.
The relationship gives User a new attribute posts which is a list of
all of the posts whose author_ids are the user's. The backref means
that Post now has an author attribute so you can easily get it's
author's User object. lazy = dynamic just means that a query object
is returned instead of a list of objects so you can then call something
like filter_by on it.