sillo.record and the admin panel. What the ORM adds on top of Tortoise, how the two fit together, and a map of this section.
Two things live here: Record, Sillo’s ORM, and the admin panel built on top of it.
from sillo.record import Modelfrom tortoise import fields
class Post(Model): id = fields.IntField(pk=True) title = fields.CharField(max_length=200) body = fields.TextField()post = await Post.create(title="Hello", body="…")recent = await Post.active().order_by("-created_at").limit(10)print(post.to_dict())Record is Tortoise, plus a layer
Section titled “Record is Tortoise, plus a layer”sillo.record is a convenience layer over
Tortoise ORM. It does not fork Tortoise, wrap
its query compiler, or replace its connection handling.
Every Tortoise feature (fields, querysets, Q objects, relations, prefetching,
aggregation, raw SQL) behaves exactly as the Tortoise documentation describes,
because it is Tortoise underneath.
You should not need to go and read that documentation. This section covers the whole surface: fields, relationships, the queryset API, the lookups, aggregation, eager loading and raw SQL, as Sillo exposes it, with the Record layer in place rather than described separately.
What Record adds is the layer Django and Laravel developers reach for on day one:
| A base model | Timestamps and soft deletes already wired |
| Mass-assignment control | fillable and guarded |
| Casting | Attributes converted on the way in and out |
| Scopes | Reusable query fragments, local and global |
| Events | Lifecycle hooks and observers |
| Collections | Chainable operations on a result set |
| Factories | Test data without fixtures |
| Transactions | A context manager, with savepoints |
| Migrations | Generated, reviewable, and applied by the CLI |
The section
Section titled “The section”Getting a database
Section titled “Getting a database”- Setup:
setup_record, and the connection lifecycle - Configuration:
DatabaseConfig, URLs, backends, pooling
Models
Section titled “Models”- Models: the base class, serialisation, fetch shortcuts
- Field reference: every field type and its arguments
- Record’s own fields:
PasswordField,SlugField,ULIDField - Relationships: foreign keys, one-to-one, many-to-many
- Meta, indexes & constraints: table options and the schema
- Mass assignment:
fillable,guarded,update_from_dict - Mixins: composable behaviours, including soft deletes
- Casting:
_castsand the cast registry - Scopes: local and global query scopes
- Events: lifecycle hooks and observers
Querying
Section titled “Querying”- The QuerySet API: every method, and when the query runs
- Field lookups: everything you can put after
__ - Filtering with Q and F: OR, negation, column references
- Aggregation:
annotate, counting, grouping - Eager loading:
select_related,prefetch_related - Values & projections: fetching less than a whole row
- Raw SQL: when the ORM cannot express it
Reading and writing
Section titled “Reading and writing”- Queries: the helpers Record adds around a queryset
- Collections: working with a result set
- Pagination: pages, and the framework’s paginators
- Bulk operations:
bulk_create,upsert,bulk_upsert - Transactions: atomicity and savepoints
- Connections: replicas, routing and pooling
Testing and schemas
Section titled “Testing and schemas”- Factories: building instances for tests
- Seeding and fixtures:
SeederandFixtureLoader - Pydantic: generating schemas from models
- Exception handlers: database errors as HTTP responses
Migrations
Section titled “Migrations”- Migrations: what they are, and how they are generated
- Applying them: the deployment shape
- Programmatically: without the CLI
The admin panel
Section titled “The admin panel”- Overview: mounting it
- Registering models:
ModelAdmin - Customising: lists, filters, search, forms
- Permissions and auth: who gets in, and to what
Honesty about maturity
Section titled “Honesty about maturity”Some parts of this package are thinner than a first read suggests, and you are
better served knowing which up front than finding out during an incident. Where
that is true, the page says so plainly, including what a helper needs before it
will do what its name says. See for example the encrypted
cast.