Models
Models are the way to persist data in a database. Hyperflask uses SQLORM, an easy to use ORM that does not abstract away SQL. By default, Hyperflask apps are configured to use SQLite which has been tuned for great web app performance.
Info
This feature is provided by Flask-SQLORM
Defining models
Models are classes inheriting from Model
. To define which table they represent, use the table
class property.
To define column mapping, define properties via annotations. The type used will be converted to an sql type.
For more control over the mapping, you can use instantiate Column()
objects.
Example app/models.py:
from hyperflask.factory import db
class Task(db.Model):
table = "tasks"
id: int
title: str = db.Column(type="varchar(20)") # set the column type (used in create_table())
done = db.Column("completed", bool, default=False) # no annotation, column name is "completed" but property name will be "done"
Once columns are defined via annotations or db.Column
properties, they are accessible as class and instance properties.
Read more in sqlorm documentation
Persisting data
Manipulate model objects as you would with any python objects. The following methods help you execute DML statements:
save()
executesinsert()
orupdate()
depending on the fact that the object has a primary key or notinsert()
executes an insert statementupdate()
executes an update statementdelete()
executes a delete statementrefresh()
executes a select statement (same asget()
) and updates the object attribute valuescreate()
a class method to create and insert an object in one line
These methods (apart from create()
) return a boolean indicating if the operation was performed.
The data used to insert or update will be limited to "dirty" attributes, which means attributes that have been modified since the last DML statement. Setting an attribute will automatically flag it as dirty.
To call this methods, you must be in a transaction context. Use the db object to initiate one.
from app.models import db, Task
with db:
task = Task.create(title="my task") # INSERT INTO tasks (title) VALUES ('my task')
task = Task()
task.title = "my task"
task.save() # INSERT INTO tasks (title) VALUES ('my task')
# same as task.insert()
task = Task.get(1)
task.title = "renamed task"
task.save() # UPDATE tasks SET title = 'renamed task' WHERE id = 1
# same as task.update()
task = Task.get(2)
task = Task.get_or_404(2) # raise a 404 error if object not found
task.delete() # DELETE FROM tasks WHERE id = 2
task = Task()
task.id = 1
task.refresh() # SELECT * FROM tasks WHERE id = 1
Read more in sqlorm documentation
Querying data
The following methods can be used to query data:
query()
executes the provided statement usingfetchhydrated()
find_all()
constructs a select statement based on the provided arguments and executes usingquery()
find_one()
same asfind_all()
but only returns the first rowget()
to find one row by primary key
The two finder methods can take as argument a where condition (sql string) or keyword arguments representing attributes to filter by.
It is not needed to provide a transaction context to call these methods.
todos = Task.query("SELECT * FROM tasks WHERE NOT done")
todos = Task.find_all("NOT done")
todos = Task.find_all(Task.done==False)
todos = Task.find_all(done=False)
task = Task.find_one("id=1")
task = Task.get(1)
Read more in sqlorm documentation
Rendering model objects
Model objects can be rendered using any jinja macro (components being macros). Set the __macro__
class property to the name of the macro.
class Task:
__macro__ = "Task"
The macro will be provided an obj
property. This can be overriden using the syntax MacroName(property)
.
class Task:
__macro__ = "Task(task)"
In app/components/Task.html
:
<label>
<input type="checkbox">
{{props.task.title}}
</label>
Once defined, you can "print" your objects and result sets directly in your templates:
---
page.tasks = Task.find_all()
---
{{tasks}}
---
page.task = Task.get(1)
---
{{task}}
Managing the schema
Hyperflask will automatically create missing tables on start. It will not however alter existing tables.
To manage your schema, you can use migrations as described in the sqlorm documentation.
Initialize migrations using the following command: uv run hyperflask db init-migrations --set-version
.
This will create a migration file at data/migrations/000_initial.sql.
Once migrations are activated, they will be auto applied on application start. This can be disabled using init_database: false
in the config, in which case run your migrations using uv run hyperflask db migrate
.
To create a new migration version for a new model use: uv run hyperflask db init-migrations MyModel
Choosing a database
By default, Hyperflask uses SQLite with optimizations for usage in web applications.
If SQLite is not a good fit for your app, we recommend PostgreSQL. In this case install the postgresql dbapi adapter using uv add psycopg[binary]
then set the sqlorm_uri
configuration parameter to the postgresql connection string.