Skip to main content

crudkit

A metadata-driven CRUD framework for Django. Define models; get typed object IDs, soft delete, merge, change logging, an activity feed, saved views, AI-populated fields and a generic REST API — without writing per-model serializers, viewsets or routes.

Core concepts

Every CrudKit model inherits BaseCrudKitModel and declares a 3-letter TYPE_ID. Object IDs are rendered as <TYPE_ID><pk> (e.g. CUS42) across the API and UI.

from django.db import models
from crudkit.models import BaseCrudKitModel

class Customer(BaseCrudKitModel):
    TYPE_ID = "CUS"
    name = models.CharField(max_length=255)

    class CrudKitSettings(BaseCrudKitModel.CrudKitSettings):
        search_fields = ["name"]

Included with every model: created_by/updated_by/created_at/updated_at audit columns, a deleted soft-delete flag (soft_delete()), merge support (delete_and_merge_with()), a change log, an activity feed (FeedItem), external-system sync (ExternalObject), saved views/layouts, and optional AI fields (AISummaryField, AICategoryField, AIBooleanField, AITagsField, AIForeignKeyField) populated asynchronously via Celery and pydantic-ai.

Installation

pip install crudkit[api]          # REST API included
pip install crudkit[assistant]    # + AI assistant sidebar (Channels)
pip install crudkit[mcp]          # + remote MCP server with OAuth (e.g. a Claude connector)
INSTALLED_APPS = [
    ...,
    "rest_framework",
    "crudkit",
    "crudkit_assistant",  # optional
]

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "crudkit_api.permissions.CrudKitModelPermissions",
    ],
    "DEFAULT_PAGINATION_CLASS": "crudkit_api.pagination.CrudKitPagination",
    "DEFAULT_FILTER_BACKENDS": ["crudkit_api.filters.BasicFilter"],
    "PAGE_SIZE": 50,
}

# urls.py — one include registers a full CRUD API for every TYPE_ID model
urlpatterns = [path("api/v1/", include("crudkit_api.urls"))]

The assistant sidebar talks over a WebSocket at ws/assistant/, next to the realtime ws/changes/ socket; route both in your ASGI application (see examples/demo/demo/asgi.py):

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": URLRouter(
        crudkit_api.routing.websocket_urlpatterns + crudkit_assistant.routing.websocket_urlpatterns
    ),
})

The sidebar sees what the user has on screen — the open record, or the list with its saved view, search, filters, visible and selected rows — and can search and read across record types. It only proposes changes (to any record the user may change); each one waits for Confirm. Conversations are stored per user (AssistantConversation, ASC) and resume after a reload. The SPA only shows the assistant when an AI model is configured (CRUDKIT_AI_MODEL or CRUDKIT_AI_MODEL_FACTORY).

CrudKit requires the standard Django model permissions for every API and assistant operation. Projects that need row-level rules can override CrudKitSettings.get_authorized_queryset(user, queryset, action); action is one of view, add, change, or delete. Model actions require change permission and can be narrowed further with CrudKitSettings.has_action_permission(user, instance, action_name).

Settings

Setting Purpose
CRUDKIT_AI_MODEL pydantic-ai model string (e.g. "mistral:mistral-large-latest") enabling AI fields/assistant
CRUDKIT_AI_MODEL_FACTORY dotted path to an async context manager yielding a pydantic-ai Model (advanced)
CRUDKIT_USER_PROFILE_ADAPTER dotted path to a class supplying preferred language + avatar images for users
CRUDKIT_EXTRA_GENERIC_RELATIONS project models surfaced as generic relations in object metadata
CRUDKIT_DEFAULT_CURRENCY, CRUDKIT_CURRENCY_CHOICES currency configuration for MoneyField
CRUDKIT_GRAVATAR_FALLBACK_EMAIL fallback avatar email (gravatar mp default otherwise)
CRUDKIT_DASHBOARD_WIDGETS dotted path to a dashboard_for_user(user) widget provider
CRUDKIT_ASSISTANT_NAME, CRUDKIT_ASSISTANT_SYSTEM_PROMPT, CRUDKIT_ASSISTANT_AVATAR_URL assistant branding
CRUDKIT_FRONTEND_CONFIG dict injected into the bundled SPA at runtime (app_name, logo_url, ...)
CRUDKIT_FRONTEND_LOGIN_REQUIRED redirect anonymous users of the SPA view to LOGIN_URL
CRUDKIT_MCP_SERVER_NAME serverInfo.name reported by the MCP server (default "crudkit")
CRUDKIT_MCP_WRITE_ENABLED offer the write OAuth scope and the create/update/action/note tools (default False)
CRUDKIT_MCP_MODELS list of TYPE_IDs to expose over MCP (default: every project model)
CRUDKIT_MCP_EXTRA_TOOLS dotted paths to crudkit_mcp.tools.Tool instances; added to, or replacing, the generated tools
CRUDKIT_MCP_BASE_URL public origin for OAuth metadata URLs when the request's host/scheme is wrong (e.g. behind a proxy)

MCP server

crudkit_mcp exposes the models as a remote MCP server (streamable HTTP) with its own OAuth 2.1 authorization server (dynamic client registration, PKCE, rotating refresh tokens), so it can be added as e.g. a Claude connector by URL.

INSTALLED_APPS = [..., "crudkit_mcp"]
LOGIN_URL = "/login/"  # the OAuth consent page needs a session login

urlpatterns = [
    path("api/v1/", include("crudkit_mcp.urls")),       # /api/v1/mcp, /api/v1/oauth/...
    path("", include("crudkit_mcp.well_known_urls")),   # /.well-known/oauth-* (site root)
    ...,
]

The tool set is fixed — records are addressed by TYPE_ID and CK-ID, so the same tools serve any project:

Tool
describe_types the record types; for one type, its filters, writable fields, actions, permissions and saved views
search free-text search across types, returning {id, label}
list_records one type or saved view (its filters, ordering and columns), a filters object (keys from describe_types), query, order_by, limit, offset
get_record one record by ID, with its feed, change log and available actions

With CRUDKIT_MCP_WRITE_ENABLED and a token granted the write scope, four more: create_record, update_record, run_action and add_note.

Every call is filtered through the token user's model, row and action permissions, so a type the user can't view isn't listed and can't be read. CrudKit's own models and Django's (including User) are never exposed; narrow the rest with CRUDKIT_MCP_MODELS = ["CMP", "PER", ...] or opt a single model out with CrudKitSettings.mcp_exclude = True.

Bundled frontend

The wheel ships the built CrudKit web SPA. To serve it, add the app, the context processor, a config dict, and a catch-all url include (last!):

INSTALLED_APPS = [..., "crudkit_frontend"]

TEMPLATES = [{
    ...,
    "OPTIONS": {"context_processors": [
        ...,
        "crudkit_frontend.context_processors.crudkit_config",
    ]},
}]

CRUDKIT_FRONTEND_CONFIG = {"app_name": "My App"}  # injected into the SPA at runtime
CRUDKIT_FRONTEND_LOGIN_REQUIRED = False  # True → redirect anonymous users to LOGIN_URL

# urls.py — must be the LAST pattern; everything unmatched serves the SPA
urlpatterns = [..., path("", include("crudkit_frontend.urls"))]

Static assets are served by django.contrib.staticfiles (or WhiteNoise et al.) from crudkit_frontend/static/, under whatever STATIC_URL the project uses — nothing assumes the default static/ prefix. Contributors hacking on the SPA itself run the Vite dev server from ../frontend against any CrudKit backend; npm run build there regenerates the bundled assets.

Running the tests

cd backend
uv sync --all-extras
uv run manage.py test crudkit crudkit_api crudkit_assistant crudkit_frontend crudkit_mcp tests

License

MIT

Metadata

Release files for crudkit 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for crudkit 0.5.0
File Size Uploaded Provenance
crudkit-0.5.0.tar.gz 664.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for crudkit 0.5.0
File Interpreter ABI Platform
crudkit-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / crudkit-0.5.0.tar.gz

Download URL crudkit-0.5.0.tar.gz
Size 664.3 kB
Tags Source
SHA-256 checksum
How to use checksums
4d4fa071e47f381e3e602823d343273f2119289edb9d6bd6b1e38482982c3dc8
BLAKE2b-256 checksum
How to use checksums
35d1ec237c8623c6060a4da5fb45acf86d91f5a2c6897952f1c9670d0f2796cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 27, 2026.

Transparency log

Release files / crudkit-0.5.0-py3-none-any.whl

Download URL crudkit-0.5.0-py3-none-any.whl
Size 653.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
450793a7099543d41b6772a1862995e54f280385da126c8869202060b1011f21
BLAKE2b-256 checksum
How to use checksums
f2434532afa2a943112a48aa2c574a76e3b873e5cdc4594a9063a04f32f47acd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 27, 2026.

Transparency log

Security for crudkit 0.5.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.

Select from 2 files

Attestation for crudkit-0.5.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 Sep 27, 2026.

Transparency log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page