kajenn-db
Database handlers for kajenn: the
implementations of kajenn's DbHandlerBase contract, one module per backend.
kajenn_db.asqueel.AsqueelDbHandler— asqueel 0.9.kajenn_db.sqlalchemy.SqlAlchemyDbHandler— SQLAlchemy 2, a probe.kajenn_db.django.DjangoDbHandler— Django, a probe.
The probes are incomplete by declaration: they run kajenn's handler contract on another backend and stop there (see Probes).
Install
pip install kajenn-db[asqueel]
pip install kajenn-db[sqlalchemy]
pip install kajenn-db[django]
Importing a backend module without its extra raises an ImportError that
names the command above.
Declare an asqueel database
from kajenn import AsgiServer
from kajenn.config import AsgiConfigBuilder
from kajenn_db.asqueel import AsqueelDbHandler
class Configuration(AsgiConfigBuilder):
default_config = False
def main(self, root):
cfg = root.configuration()
dbs = cfg.databases(handler_class=AsqueelDbHandler)
dbs.database(code="default", source="shop.xml")
Each database accepts:
source— anythingAsqueelDb(source, parents=...)accepts: a recipe, a registered name, a file, adbnode.parents— passed toAsqueelDbas it is.db_class— anAsqueelDbsubclass to build instead ofAsqueelDb.
The server builds one AsqueelDbHandler and hands it every database declared
with it. No database is opened at declaration time.
One session per call
request.db(name)takes the mainSessionDbof the call (db.session()), once per name per call.- At the end of the call the handler closes the named sessions of the call, then the main one. Work not committed is rolled back, and asqueel logs a warning.
- In 0.1.0 a session opens with no environment:
currentUserisNone, andworkdateandlocaletake asqueel's defaults.
Async routes
An async def route takes request.async_db(name): the async view of the same
main SessionDb. Its I/O methods are awaitable and run on the server pool, so
the event loop keeps serving other calls.
@route()
async def add(self, _request=None):
db = _request.async_db()
notes = db.table("app.note")
await notes.insert({"id": "n1", "text": "first"})
await db.commit()
return await notes.query(columns="$id").fetchPkeys()
The view has three levels, plus the cursor:
- session — awaitable:
execute,executemany,commit,rollback,vacuum,analyze.table(name)andsession(name)return views. - table — awaitable:
insert,update,delete,insertOrUpdate,lock.query(...)returns a view. - query — awaitable:
fetch,fetchRecord,fetchPkeys,fetchAsJson,fetchAsDict,fetchAsBag,fetchGrouped,count,delete,update,execute,cursor. - cursor — returned by
executeandcursor; awaitable:fetchone,fetchmany,fetchall,close.
Every other I/O method raises UnsupportedFeatureError on the view, naming
what to use instead: iterfetch and serverfetch name cursor(); close
belongs to the end of the call; migrate and migration_plan belong to the
handler. Methods that do no I/O pass through as asqueel has them.
Rules:
- In an async route, never call a
SessionDbmethod onrequest.db(): it runs on the event loop and blocks every other call, with no error. - Consume a cursor before the call ends: the end of the call closes the session, and the cursor with it.
- A streaming response that reads a cursor registers the release after its last chunk.
query(...).fetch()loads every row in memory. For large results, read a server-side cursor (cursorname) in chunks.
Serialising rows
kajenn serialises a dict/list result with genro_tytx.json_dumps
(genro-tytx 0.16.4). Rows returned as asqueel gives them do not survive it:
- a row of
fetch()orfetchAsDict()(NamedList) becomes a bare list of values: the column names are lost; date,datetime,DecimalandUUIDvalues raiseTypeError.
Convert in the route: dict(row.items()) keeps the column names, and the
values of those four types need converting too. fetchAsJson() returns the
JSON text itself: kajenn serves a str result as text/plain. The handler
converts nothing, so a query gives the same types in a sync and in an async
route.
Probes
SQLAlchemy
from kajenn_db.sqlalchemy import SqlAlchemyDbHandler
dbs = cfg.databases(handler_class=SqlAlchemyDbHandler)
dbs.database(code="default", url="sqlite:///shop.sqlite")
url— the SQLAlchemy database URL; one sharedEngineper database, disposed at server shutdown.request.db(name)takes aSessionof the call; the end of the call closes it, and a closedSessionrefuses any further use.request.async_db(name)is kajenn'sAsyncDbViewover the sameSession, so one call has one transaction in a sync and in an async route. Awaitable:execute,scalars,scalar,get,get_one,merge,flush,refresh,commit,rollback. Every other attribute passes through as it is, I/O included:query()and itsQuery.all(),delete(),connection()andbegin()run on the event loop and block it, with no error. Unlike the asqueel view, the probe refuses nothing.- A
Resultreads its rows from the cursor when it is iterated, on the event loop: keep the results of an async route small. - No
migrate: SQLAlchemy has no migration of its own (Alembic is a separate tool).
Django
from kajenn_db.django import DjangoDbHandler
dbs = cfg.databases(handler_class=DjangoDbHandler)
dbs.database(code="default", alias="default")
- Django is configured by the process (
DJANGO_SETTINGS_MODULEorsettings.configure(), thendjango.setup()), not by the handler. alias— a key of Django'sDATABASES; the database's own name by default.request.db(name)takesconnections[alias], the connection Django's ORM uses on the calling thread: the ORM in a sync route works on it. The end of the call closes it on the same thread.- Sync routes only. Django binds a connection to the thread that opened it,
and kajenn takes a database of an
async defroute on the event-loop thread:request.async_db(name)raisesNotImplementedError. An async route uses Django's async ORM, whose connections kajenn does not release. - Not for sync work outside a route either: a task, a kbus call or a WSX
message takes the connection on one pool thread, and kajenn releases it with
server.run_sync, possibly on another. Django refuses to close it there (DatabaseError, logged by kajenn) and the connection stays open on the thread that opened it. migration_plan(name)lists the migrations not yet applied, asapp_label.name;migrate(name)applies them (manage.py migrate --database alias). Both are sync and close the calling thread's connection at the end.
Metadata
Release files for kajenn-db 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| kajenn_db-0.1.0.tar.gz | 24.0 kB | With provenance | Details for kajenn_db-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| kajenn_db-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for kajenn_db-0.1.0-py3-none-any.whl |
Total release size: 39.5 kB
Release files / kajenn_db-0.1.0.tar.gz
| Download URL | kajenn_db-0.1.0.tar.gz |
|---|---|
| Size | 24.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
68b4fb8289e574f993d5a58a4fe6b582ca4cba74ab159e4ef891eb41c52ab1dd
|
|
BLAKE2b-256 checksum How to use checksums |
3dc066e366bdf617b64f084bedeaac75993535cad6503e1e7c61d097edb278ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / kajenn_db-0.1.0-py3-none-any.whl
| Download URL | kajenn_db-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
909cd5caae563faabe21986d404f22c276f61898670f5c46a19d7b16860de9de
|
|
BLAKE2b-256 checksum How to use checksums |
17eb97d8ae41a90a6d1c0216d7e2e4b6d70ce419573ce944b0caa267dc84818a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for kajenn-db 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for kajenn_db-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.