D1Q — SQL Factory
D1Q is the single source of truth for all D1 SQL in django_cfg. You define a table once using D1Table, then call D1Q methods to get (sql, params) tuples — no raw SQL strings anywhere.
Defining a Table
from django_cfg.modules.django_cf.core.d1_query import D1Column, D1Index, D1Q, D1Table
MY_TABLE = D1Table(
name="my_records",
columns=[
D1Column("id", "TEXT", primary_key=True),
D1Column("api_url", "TEXT", not_null=True),
D1Column("status", "TEXT", not_null=True, default="'pending'"),
D1Column("created_at", "TEXT", not_null=True),
D1Column("updated_at", "TEXT", not_null=True),
],
pk=["id", "api_url"], # composite primary key
upsert_update=["status", "updated_at"], # columns updated on conflict
indexes=[
D1Index("idx_my_records_status", ["api_url", "status"]),
],
)D1Column fields
| Field | Type | Description |
|---|---|---|
name | str | Column name |
type | str | SQLite type: TEXT, INTEGER, REAL, BLOB |
primary_key | bool | Part of the primary key |
not_null | bool | NOT NULL constraint |
default | str | None | SQL default expression (e.g. "'pending'", "0") |
D1Index fields
| Field | Type | Description |
|---|---|---|
name | str | Index name |
columns | list[str] | Columns to index |
unique | bool | UNIQUE index |
DDL Methods
# CREATE TABLE IF NOT EXISTS ...
create_sql = D1Q.create_table(MY_TABLE)
# CREATE INDEX IF NOT EXISTS ... (one per index defined)
index_sqls: list[str] = D1Q.create_indexes(MY_TABLE)Combine them in _get_schema_statements() when subclassing BaseD1Service:
def _get_schema_statements(self) -> list[str]:
return [
D1Q.create_table(MY_TABLE),
*D1Q.create_indexes(MY_TABLE),
]DML Methods
All DML methods accept a Pydantic model instance (or any object with matching attributes) and return (sql: str, params: list).
upsert
Insert or update on conflict using upsert_update columns:
sql, params = D1Q.upsert(MY_TABLE, my_model_instance)upsert_increment
Upsert and atomically increment a counter column on conflict:
sql, params = D1Q.upsert_increment(
MY_TABLE,
my_model_instance,
increment_col="occurrence_count",
)Used by MonitorSyncService to deduplicate server events.
insert_ignore
Insert only if the row does not already exist:
sql, params = D1Q.insert_ignore(MY_TABLE, my_model_instance)Used by MonitorSyncService for frontend events (append-only).
delete_where
Delete rows matching an exact dict of column values:
sql, params = D1Q.delete_where(MY_TABLE, {"api_url": url, "is_resolved": "1"})delete_where_raw
Delete with a raw SQL WHERE clause (use sparingly):
sql, params = D1Q.delete_where_raw(MY_TABLE, "created_at < ?", [cutoff_iso])Executing Queries
All queries are executed via CloudflareD1Client:
from django_cfg.modules.django_cf.core import BaseD1Service
class MyService(BaseD1Service):
def _get_schema_statements(self) -> list[str]:
return [D1Q.create_table(MY_TABLE), *D1Q.create_indexes(MY_TABLE)]
def upsert_record(self, data: MyModel) -> None:
self._ensure_schema()
sql, params = D1Q.upsert(MY_TABLE, data)
self._get_client().query(sql, params)See Also
- Overview — Architecture and BaseD1Service
- User Sync — Real-world D1Q usage in UserSyncService
- Monitor — Server Capture —
upsert_incrementandinsert_ignorein action
TAGS: D1Q, d1-table, sql-factory, django_cf DEPENDS_ON: [django-cf/overview]