Skip to main content

Safe natural-language querying over explicitly registered Django models.

Project description

Django AskLens

Django AskLens is a reusable Django + DRF package for safe natural-language querying over explicitly registered Django models.

AskLens does not let an LLM write SQL. It asks a provider for structured JSON, validates the plan against your registered catalog and permissions, compiles a read-only Django ORM query, executes with limits, and returns table/chart-ready JSON.

Status: alpha. APIs may change before a stable release.

What it provides

  • Explicit semantic resource registration.
  • Permission-scoped catalog and capabilities endpoints.
  • Strict Pydantic QueryPlan validation.
  • ORM-only list and aggregate query execution.
  • Dummy provider for deterministic tests and demos.
  • OpenAI-compatible live provider adapter.
  • Query-run audit records.
  • Frontend-agnostic columns + data JSON output.
  • Optional packaged browser UI for demos/reference use.

Quickstart

Install:

python -m pip install django-asklens

Add the app and DRF:

INSTALLED_APPS = [
    # ...
    "rest_framework",
    "django_asklens",
]

Mount the API:

from django.urls import include, path

urlpatterns = [
    path("", include("django_asklens.api.urls")),
]

Run migrations for AskLens audit records:

python -m django migrate asklens

Register a resource during app startup:

from django_asklens import Metric, register
from shop.models import Order


def visible_orders(request):
    if not getattr(request.user, "is_authenticated", False):
        return Order.objects.none()
    return Order.objects.filter(account__memberships__user=request.user)


register(
    model=Order,
    name="orders",
    label="Orders",
    description="Orders visible to the current user.",
    default_date_field="created_at",
    fields={
        "id": {"label": "Order ID"},
        "status": {"label": "Status"},
        "created_at": {"label": "Created date"},
        "customer.email": {
            "label": "Customer email",
            "sensitive": True,
            "requires_permission": "customers.view_pii",
        },
        "total_cents": {"label": "Total in cents"},
    },
    metrics=[
        Metric("order_count", op="count", field="id", label="Orders"),
        Metric("revenue", op="sum", field="total_cents", label="Revenue"),
    ],
    requires_permission="orders.view_reports",
    base_queryset=visible_orders,
)

requires_permission on the resource gates catalog visibility and query validation for the whole resource. Field-level requires_permission gates individual fields such as PII.

Start with the deterministic dummy provider:

DJANGO_ASKLENS = {
    "LLM_BACKEND": "dummy",
    "DUMMY_PLANS": {
        "Show orders by status": {
            "resource": "orders",
            "intent": "aggregate",
            "group_by": [{"field": "status"}],
            "metrics": [{"name": "order_count", "op": "count", "field": "id"}],
            "limit": 100,
            "visualization": {"type": "bar", "x": "status", "y": "order_count"},
        }
    },
}

Ask through the API:

POST /asklens/query/
Content-Type: application/json

{"question": "Show orders by status"}

Successful data responses include:

{
  "run_id": 1,
  "question": "Show orders by status",
  "response_type": "query",
  "plan": {"resource": "orders", "intent": "aggregate", "limit": 100},
  "columns": [{"key": "status", "label": "Status", "type": "string"}],
  "data": [{"status": "paid", "order_count": 12}],
  "row_count": 1,
  "result_metadata": {
    "limit": 100,
    "limit_scope": "groups",
    "limit_reached": false
  },
  "visualization": {"type": "bar"}
}

Help questions such as show me example queries return response_type: "capabilities" with suggestions instead of running a database query.

Building a UI

AskLens is API-first. Build your own UI with React, Vue, HTMX, Django templates, a mobile client, or any chart/table library by rendering the returned columns and data arrays.

The packaged frontend is optional and intended as a dependency-free demo/reference UI. Projects that need product-specific layout, charts, saved queries, or workflows should call the API directly. See Building a custom AskLens UI.

Optional packaged frontend

If you want the built-in reference UI:

urlpatterns = [
    path("", include("django_asklens.api.urls")),
    path("", include("django_asklens.frontend.urls")),  # /asklens/ui/
]

Gate the page for selected users with:

DJANGO_ASKLENS = {
    "FRONTEND_PERMISSION_CHECK": "myapp.permissions.can_use_asklens_frontend",
}

API route permissions still apply to every API call. The frontend permission check only controls whether the packaged page can load.

Live providers

The default backend is dummy and makes no network calls. To use an OpenAI-compatible provider:

import os

DJANGO_ASKLENS = {
    "LLM_BACKEND": "openai_compatible",
    "LLM_BASE_URL": "https://api.openai.com/v1",
    "LLM_API_KEY": os.environ["OPENAI_API_KEY"],
    "LLM_MODEL": "gpt-4.1-mini",
    "LLM_TEMPERATURE": 0,
}

Gemini can be used through its OpenAI-compatible endpoint:

DJANGO_ASKLENS = {
    "LLM_BACKEND": "openai_compatible",
    "LLM_BASE_URL": "https://generativelanguage.googleapis.com/v1beta/openai",
    "LLM_API_KEY": os.environ["GEMINI_API_KEY"],
    "LLM_MODEL": "gemini-2.5-flash",
    "LLM_TEMPERATURE": 0,
}

Live provider tests are opt-in and skipped by default. See Provider configuration.

Safety posture

  • Only explicitly registered resources and fields are queryable.
  • Every query starts from the resource base_queryset(request) hook.
  • Sensitive fields are hidden unless explicitly permissioned.
  • Provider output is untrusted and always validated before execution.
  • AskLens executes read-only Django ORM queries only.
  • AskLens does not execute LLM-generated SQL.
  • AskLens does not create, update, or delete application data.
  • AskLens does not send database rows, sample values, secrets, credentials, or .env content to providers by default.
  • Query runs are audited.

Review the security checklist and production checklist before enabling AskLens outside local development.

Alpha scope and safety boundaries

  • APIs may change before a stable release.
  • AskLens supports read-only list and aggregate questions over explicitly registered resources.
  • Query quality depends on clear resource, field, description, and metric registration.
  • Live provider behavior varies by model and prompt complexity; DummyProvider remains the deterministic default for tests and demos.
  • SQL generation/execution is intentionally out of scope. AskLens uses validated QueryPlan JSON and Django ORM compilation only.
  • Writes and mutations are intentionally out of scope.
  • Server-side saved queries, dashboard builders, and a dedicated help endpoint are not part of the alpha package surface.
  • The packaged frontend is a reference/demo UI; custom product UIs should call the API directly.
  • Read-only replica/database routing is a host-project deployment concern in alpha.
  • Current package metadata and CI target Django 6.x.

Documentation

Development

Use Python 3.12 or newer and uv for local development.

uv sync --group dev
uv run pytest
uv run ruff check .
uv run ruff format --check .

The package remains standards-based and setuptools-backed; uv is used for contributor workflows, not as a runtime dependency.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_asklens-0.1.0a0.tar.gz (97.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_asklens-0.1.0a0-py3-none-any.whl (89.6 kB view details)

Uploaded Python 3

File details

Details for the file django_asklens-0.1.0a0.tar.gz.

File metadata

  • Download URL: django_asklens-0.1.0a0.tar.gz
  • Upload date:
  • Size: 97.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for django_asklens-0.1.0a0.tar.gz
Algorithm Hash digest
SHA256 c8534fdbb06ed6b684e49090e551db7e4c2ba0a2ab01083187d5737fa4c6be0f
MD5 ba52b0db4f575bd6f3f249c44407173b
BLAKE2b-256 e0dfd59ee92b4fbb829d10adb5199565c686e6e956732f11da1f450a3884a9ae

See more details on using hashes here.

File details

Details for the file django_asklens-0.1.0a0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_asklens-0.1.0a0-py3-none-any.whl
Algorithm Hash digest
SHA256 23cdd78170603f49a701f3210ee4e8743158225bc3b536a5549ae9881c05dfa3
MD5 5987048b8164dcce55bb62d056d8344a
BLAKE2b-256 51ddf0623490dfd3adcc448ad5500642919c4d23d94c209e0f497e05c541023e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page