Models

Active Record models live in app/models/ and inherit future.models.Model. Class annotations are the source of truth for columns — migrations and seeders are generated from them.

Define a model

future make:model Stock
from future.models import Model

class Stock(Model):
    # __table__ = "stocks"        # optional; default is tableized class name (Stock → stocks)
    # __connection__ = "default"  # name from DATABASES / Connections

    id: str
    name: str
    symbol: str
    instrument_id: str
    price: float | None

Register connections in Configuration / Database before using the model at runtime or in CLI.

Connections

__connection__ selects which entry in DATABASES the model uses. Default is "default", which resolves to the name in DATABASES["default"] (e.g. "sqlite"). Set it to any other registered key to put a model on a different store:

from future.models import Model

class Stock(Model):
    __connection__ = "default"   # → DATABASES["default"] → e.g. sqlite

class Trade(Model):
    __connection__ = "mysql"     # → DATABASES["mysql"]

class Event(Model):
    __connection__ = "postgres"  # → DATABASES["postgres"]

future make:migration copies the model’s __connection__ onto the migration class. future migrate / rollback apply each migration on that connection (migration history is tracked per connection). Seeders call the model’s own save() / queries, so they follow the model connection automatically.

Register every named connection in DATABASES before use — see Database.

Annotations drive generators

After annotations are set:

future make:migration Stock   # → database/migrations/…_create_stocks.py from annotations
future make:seed Stock        # → database/seeds/StockSeeder.py from annotations

# or every annotated model under app/models/:
future make:migrations
future make:seeds             # skips seed files that already exist

Then apply / run:

future migrate
future seed                   # all seeders
future seed StockSeeder       # one class name

Prefer either make:migration Stock or make:migrations for the same model in one session — not both (duplicate files).

Generated migration (from the Stock annotations above):

from future.migrations.Migration import Migration
from future.migrations.Schema import Schema

class CreateStocks(Migration):
    __connection__ = "default"

    def up(self):
        with Schema.create("stocks") as table:
            table.id()
            table.string("name")
            table.string("symbol")
            table.string("instrument_id")
            table.float("price").nullable()

    def down(self):
        Schema.drop("stocks")

Generated seeder uses the model fields (Faker stubs):

from faker import Faker
from future.seeds.Seeder import Seeder
from app.models.Stock import Stock

class StockSeeder(Seeder):
    def run(self):
        fake = Faker()
        for _ in range(10):
            Stock(
                id=fake.uuid4(),
                name=fake.company(),
                symbol=fake.unique.lexify(text="????").upper(),
                instrument_id=str(fake.random_int(10000, 99999)),
                price=float(fake.pyfloat(min_value=1, max_value=100)),
            ).save()

Edit generated files if you need indexes, extras, or richer seed data. Re-running generators does not replace hand-edited migrations; seeds skip existing files on make:seeds.

CRUD

stock = Stock(id="1", name="Equinor", symbol="EQNR", instrument_id="16105067", price=250.0)
stock.save()

stock = Stock.find("1")
stocks = Stock.all()
stock.price = 251.0
stock.save()
stock.delete()

Query

where / order_by return a Query — call .get() or .first() to hit the DB:

Stock.where("symbol", "EQNR").first()
Stock.where("price", ">", 100).order_by("price", "desc").get()
Stock.where("name", "like", "%Equinor%").limit(20).get()

In a controller:

async def get_stocks(self) -> Response:
    ticker = self.request.query.get("ticker")
    if ticker:
        stocks = Stock.where("symbol", ticker).get()
    else:
        stocks = Stock.all()
    return self.response.json([s.to_dict() for s in stocks], status=200)

See CLI for the full command list and Database for drivers and connection registration.