Demo application
Fastfort Freight
A city logistics platform: hubs, delivery zones, routes, vehicles, shipments, invoices. Seventeen models on PostgreSQL and PostGIS, chosen so that every capability FastFort has is exercised by a column that means something — not by a rig model with seven nameless shapes on it.
Already running
Open it without installing anything
A live instance, over the same schema and the same seeded data this page describes. Sign in and delete an invoice — the confirmation counts what goes with it before anything is written.
Nothing to type: the sign-in form arrives with
admin@fastfort.dev already in it. Press Sign in.
Restored from random.Random(20260811) every six hours, so what you
find is what this page says is there — and so anything you break is temporary.
Built on the published package
The demo installs fastfort from PyPI, exactly as the quickstart tells
you to. It is not wired to a checkout of the framework — which is the only way it
stays an honest test of what actually shipped, rather than of what is on somebody's
branch.
uv add "fastfort[sqlalchemy,postgres]" Quickstart → Everything below is running at fastfort.inventrix.uz right now, over the schema described here, with the seeded rows described here. Nothing to install to look at it.
What is in it
Every model is annotated in its own source with what it demonstrates and why it is
shaped the way it is. app/models/catalog.py is the one to read first —
it is deliberately ordinary.
Accounts
- User
- citext email, native enum, nullable boolean, JSONB preferences, M2M roles
- Role
- text[] permissions, rendered as a tag control
- API token
- UUID primary key, and a column caught by the sensitive-name rule
Catalog
- Product
- the ordinary case — plus a pgvector embedding for similarity search
- Category
- self-referential foreign key, autocomplete-backed picker
- Supplier
- inet, interval
- Tag
- a VARCHAR that is a colour only because the admin says so
Logistics
- Hub
- POINT + POLYGON on one form, and an int4range
- Zone
- MULTIPOLYGON — a zone split by a river is one row
- Route
- LINESTRING + MULTIPOINT, and an interval
- Vehicle
- geography POINT, so dwithin reads metres; macaddr
- Driver
- bit(8) weekday shifts, image upload
- Shipment
- 240 rows, six filter shapes, two bulk actions
Billing
- Customer
- daterange contract period; its delete shows CLEAR at two depths
- Invoice
- declares a cascade, so its delete shows DELETE
- Invoice line
- NOT NULL FK with no cascade — the PROTECT case
- Payment
- export in three formats
Lab
- Specimen
- one column of every type at once — 60+ fields, all eight geometry kinds
Things worth trying
Every one of these is a link into the live instance. They were dead anchors until there was something to point them at — a documentation page listing six URLs a reader cannot click is a page asking them to retype.
/admin/catalog.product/?embedding__near=[0.4,0.1,0.9]&embedding__k=5 Five nearest products, ranked. The ordering beats whatever else the page was asked for. Five kilometres from here /admin/logistics.vehicle/?last_seen__dwithin=41.3111,69.2797,5000 Metres, because the column is a geography. On a geometry the same number would be degrees. A delete that refuses /admin/billing.invoice/1/delete Counted before anything is written, and reported per related model. Every control at once /admin/lab.specimen/1/ Sixty fields: three-state boolean, interval, hstore, ranges, bit string, eight geometries. Two maps on one form /admin/logistics.hub/1/ A draggable pin for the point and a vertex editor for the catchment polygon. Turn JavaScript off /admin/catalog.product/ Sort, filter, page, search, save, delete. All of it still works. The Django half
demo/django_demo/ is a Django project whose models are
managed = False against the same tables. Run both and you have
Django's admin and FastFort's admin over one database, which is the only honest way
to compare them: same schema, same rows, same browser.
Neither project depends on the other — Django lives in its own dependency group, so
installing the FastAPI side never installs it. What came out of building both is
recorded in comments in the files themselves: eight PostGIS columns that cannot be
described to Django without GDAL and GEOS as system libraries, two many-to-manys
that filter_horizontal refuses because their association tables have
composite keys, and seven column types with no field at all.