2. Defining Models and Schema with Flask-SQLAlchemy
Welcome back to our Flask-SQLAlchemy training program! In this second post, we will dive into defining models and schema in Flask-SQLAlchemy. We’ll cover creating a model with SQLAlchemy, defining fields and data types, and establishing relationships between models.
Creating a Model with SQLAlchemy
In Flask-SQLAlchemy, a model is a Python class that represents a database table. Each instance of the class is a row in the table. To create a model, you subclass the db.Model
class and define the fields as class variables.
Here’s an example of a simple model representing a User
:
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return f'<User {self.username}>'
In this code, id
, username
, and email
are columns in the User table. The db.Column
class is a constructor for a column, and you pass the data type of the column as the first argument.
Understanding the correlation between a model, class, and table in SQL is crucial when working with an Object-Relational Mapper (ORM) like SQLAlchemy. Here’s a breakdown:
SQL Table: In a relational database, data is structured into one or more tables. A table is a collection of related data entries and consists of columns and rows. Each column in a table represents a particular type of data and each row in the table represents a single record.
Python Class: In object-oriented programming, a class is a code template used to create objects. Objects have member variables and methods associated with them. In the context of an ORM, each class represents a table in the database.
Model: A model in the context of an ORM like SQLAlchemy is a Python class that is tied to a SQL table. The model class defines the structure of the table it represents, including the column names, data types, constraints, etc. Each instance of the class represents a row in the table.
So, in summary:
A SQL table is represented in SQLAlchemy as a Python class (model). The columns of the table are defined as class variables in the model. Each row in the SQL table corresponds to an instance of the model. Here’s a simple example:
Defining Fields and Data Types
Each column in a table is defined as a db.Column
instance, which takes a SQLAlchemy data type as a required argument. SQLAlchemy provides a wide range of data types that abstract the underlying SQL data types. Here are a few examples:
db.Integer
: represents an integer.db.String(size)
: represents a string with a maximum length.db.DateTime
: represents a datetime object.db.Boolean
: represents a boolean value.
Here is an example of a model with various data types:
class BlogPost(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow)
is_published = db.Column(db.Boolean, default=False)
In this BlogPost
model, id
is an integer that serves as the primary key, title
is a string of maximum length 100, content
is a text field, created_at
is a datetime, and is_published
is a boolean.
Establishing Relationships Between Models
Flask-SQLAlchemy also allows you to define relationships between models. This is done using db.relationship
, which returns a new property that can do multiple things depending on the relationship.
Let’s consider a different example, such as an online bookstore. We can have Book
and Author
models and establish a relationship between them.
Creating Models: Book and Author
Here’s how you might define Book
and Author
models:
class Author(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
books = db.relationship('Book', backref='author', lazy=True)
def __repr__(self):
return f'<Author {self.name}>'
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
publication_date = db.Column(db.DateTime, nullable=False)
author_id = db.Column(db.Integer, db.ForeignKey('author.id'), nullable=False)
def __repr__(self):
return f'<Book {self.title}>'
Defining Fields and Data Types
In the Author
model, id
and name
are fields representing an integer and a string respectively. The books
field is a relationship field that will link to the Book
model.
In the Book
model, we have id
, title
, and publication_date
representing an integer, string, and datetime respectively. The author_id
field is a foreign key that links each book to an author.
Establishing Relationships Between Models
The db.relationship
in the Author
model creates a virtual column that SQLAlchemy will use to load data from the Book
model. The backref
argument adds a author
attribute to the Book
instances. This will load the author of a book whenever we access the author
attribute.
The lazy
argument defines when SQLAlchemy will load the data from the database. A value of True
means that SQLAlchemy will load the data as necessary in one go. This is known as lazy loading.
In the Book
model, the author_id
field is defined as a foreign key. This establishes a link from each book to an author.
The lazy
argument in SQLAlchemy determines when SQLAlchemy should load data from the database. Here are the possible options:
-
'select'
orTrue
: This is the default option and loads the data as necessary in one go. This is known as lazy loading. -
'joined'
orFalse
: This option will load the relationship in the same query as the parent using a JOIN statement. -
'subquery'
: Works like ‘joined’ but instead SQLAlchemy will use a subquery. -
'dynamic'
: This option returns a Query object instead of loading the items. This is useful for relationships with many items because you can apply additional query parameters to it, like limits and offsets.
So, if you set lazy=False
(which is equivalent to lazy='joined'
), SQLAlchemy will load the data in the same query as the parent using a JOIN statement. This can sometimes be more efficient as it reduces the number of database queries, but it can also be slower if you’re loading a lot of related data that you might not need. It’s important to understand these trade-offs and choose the best option based on the specifics of your application.
And that’s it for now! In the next post, we’ll explore how to perform CRUD operations using Flask-SQLAlchemy. Stay tuned!