System parameters & sequences¶
Almost every app needs two boring-but-essential things: somewhere to keep instance-level configuration (a company name, an API key, a default sender address), and a reliable way to mint human-readable reference numbers (INV0001, SO0001). Victor's always-installed base module gives you both as core models, plus a small SDK you call from module code.
- System parameters — a key/value store scoped to the instance, with optional encrypted-at-rest secrets.
- Sequences — named, atomic counters that produce zero-padded, prefixed reference numbers.
Both are ordinary models rendered by the generic list/form framework, so you manage their records in the app UI under Settings and read or write them from code through the SDK. You never redeclare these models in your own module — you create and read records against them.
System parameters¶
A system parameter is a single key/value setting for the instance. The system_parameter model (from the base module) has three fields.
| Field | Type | Notes |
|---|---|---|
key |
String (required) | The lookup key, e.g. company.name. Use dotted, namespaced keys. |
value |
Text | The stored value. Always a string. |
secret |
Boolean (default false) |
When set, the value is stored encrypted at rest and masked in the UI. |
Values are always strings. If you need an int, a bool, or JSON, the caller parses it — Victor stores exactly what you give it.
Reading and writing from module code¶
Import the config accessor and call get_param / set_param. Both are async and take the current session.
from victor import config
# Read: returns the stored string, or the default when the key is unset
value = await config.get_param(session, "company.name", "Acme")
# Write: creates the parameter, or updates it if the key already exists (upsert by key)
await config.set_param(session, "company.name", "Globex")
| Function | Signature | Behavior |
|---|---|---|
config.get_param |
get_param(session, key, default=None) -> str \| None |
Returns the stored string for key, or default when the key is unset. Secret values are decrypted transparently. |
config.set_param |
set_param(session, key, value, secret=False) -> None |
Upserts by key. value must be a string. Pass secret=True to store it encrypted. |
Because values are strings, parse them at the call site:
from victor import config
raw = await config.get_param(session, "billing.max_retries", "3")
max_retries = int(raw)
Real example: reading config with defaults¶
The built-in mail module reads its own settings this way, always supplying a sensible default so the module works before an admin has configured anything:
from victor import config
policy = await config.get_param(session, "mail.inbound.reply_policy", "known_senders")
default_from = await config.get_param(session, "mail.default_from", "notifications@victor.local")
This is the pattern to follow: namespace your keys under your module (mail.*, billing.*), and pass a default that keeps the module functional out of the box.
Secrets¶
Set secret=True to store a value encrypted at rest. Your module code never deals with ciphertext — get_param decrypts transparently and hands you plaintext:
from victor import config
await config.set_param(session, "api.key", "sk-123", secret=True) # stored encrypted at rest
key = await config.get_param(session, "api.key") # -> "sk-123" (plaintext for module code)
# In the System Parameters UI / records list the value is shown as ••••••
In the System Parameters list and form, secret values are masked as •••••• so they never appear on screen. Secret values are encrypted at rest, so use secret=True for anything sensitive like API keys or tokens.
Managing parameters in the UI¶
Parameters live under Settings → System Parameters, rendered by the generic framework from this view definition. You don't write this XML — it ships with the base module — but it shows the fields you'll see on the form:
<victor>
<view id="view_system_parameter_list" model="system_parameter" type="list">
<field name="key"/>
<field name="value"/>
<field name="secret"/>
</view>
<view id="view_system_parameter_form" model="system_parameter" type="form">
<group string="Parameter">
<field name="key"/>
<field name="value"/>
<field name="secret"/>
</group>
</view>
</victor>
See Views for how list/form views and menus are declared.
Sequences¶
A sequence is a named, atomic counter used to produce reference numbers like SO0001. The sequence model (from the base module) holds one counter per record.
| Field | Type | Notes |
|---|---|---|
code |
String (required) | The lookup key you pass to next_value, e.g. sale.order. |
seq_name |
String | Human-readable display name. |
prefix |
String (default "") |
Prepended to the number, e.g. SO or INV. |
padding |
Integer (default 4) |
Zero-pad width, so 1 renders as 0001. |
next_number |
Integer (default 1) |
The number the next mint will use. |
Defining a sequence¶
Create the sequence record first, in the app UI under Settings → Sequences. Give it a code (the key your code will look up), a prefix, and a padding. For example, a record with code = sale.order, prefix = SO, padding = 4 produces SO0001, SO0002, and so on.
The Sequences screen is rendered from this base-module view:
<victor>
<view id="view_sequence_list" model="sequence" type="list">
<field name="code"/>
<field name="seq_name"/>
<field name="prefix"/>
<field name="next_number"/>
</view>
<view id="view_sequence_form" model="sequence" type="form">
<group string="Sequence">
<field name="code"/>
<field name="seq_name"/>
<field name="prefix"/>
<field name="padding"/>
<field name="next_number"/>
</group>
</view>
</victor>
Define before you mint
next_value looks the sequence up by code. The record must exist before you call it — an unknown code raises ValueError.
Minting a reference number¶
Import sequences and call next_value with the sequence's code:
from victor import sequences
# 'sale.order' must be defined as a sequence first (e.g. prefix='SO', padding=4).
# Atomically claims the next number, then advances the counter.
num = await sequences.next_value(session, "sale.order") # e.g. "SO0001"
# An unknown code raises ValueError.
| Function | Signature | Behavior |
|---|---|---|
sequences.next_value |
next_value(session, code) -> str |
Atomically claims the next number for code, advances the counter, and returns prefix + zero-padded number. Raises ValueError for an unknown code. |
Numbering semantics. next_value returns the value that next_number currently points at, then advances the counter by one. The returned number is zero-padded to padding digits and prefixed with prefix. So a fresh sale.order sequence (next_number = 1, prefix = SO, padding = 4) returns SO0001 on the first call and leaves next_number at 2.
The claim-and-advance is atomic and concurrency-safe: two requests minting from the same sequence at the same time each get a distinct, gap-free number.
Using a sequence in a record action¶
The common pattern is to stamp a reference number onto a record when it's created or confirmed — for example, in a helpdesk app assigning a ticket number:
from victor import sequences
async def assign_reference(session, ticket):
ticket["reference"] = await sequences.next_value(session, "helpdesk.ticket")
return ticket
Define a sequence with code = helpdesk.ticket, prefix = TKT, padding = 5 and you'll get TKT00001, TKT00002, and so on. See Actions & menus for wiring this into record or view actions.
Field reference at a glance¶
For quick lookup, the two core models expose these fields (all provided by the base module — do not redeclare them):
# system_parameter
# key (String, required) the lookup key
# value (Text) the stored value (always a string)
# secret (Boolean, default False) encrypt at rest + mask in the UI
# sequence
# code (String, required) lookup key passed to sequences.next_value(...)
# seq_name (String) display name
# prefix (String, default "") prepended to the number, e.g. "INV" or "SO"
# padding (Integer, default 4) zero-pad width -> 0001
# next_number (Integer, default 1) the number the next mint will use
Related pages¶
- Models & fields — how models like
system_parameterandsequenceare defined. - Views — the list/form/menu XML the Settings screens are rendered from.
- Tools & server actions — where you typically call
sequences.next_valueandconfig.get_param.