← Selected work

01 / INDEPENDENT DEVELOPMENT

Django Knit/

Model-driven code generation for Django and TypeScript, without forcing applications into a prescribed architecture.

Python · Django · DRF · TypeScript

Why I built it

Django Knit started as an experiment inspired by Coalesce, a model-driven development framework. I appreciated the potential to eliminate repetitive API and frontend code, but frequently found its prescribed conventions limiting.

One particularly frustrating experience involved something as ordinary as filtering records by a list of IDs. The generated API didn't support the operation I needed until the framework added support later. A tool intended to simplify development had become an obstacle to an otherwise straightforward requirement.

Knit grew out of that experience: explore the benefits of model-driven generation while retaining the flexibility and familiar conventions of Django and Django REST Framework.

The approach

Django Knit uses explicit model registration and configuration to collect model metadata and drive code generation. The goal is predictable, readable output without requiring developers to surrender control of their applications.

From discovery to registration

The first implementation automatically discovered Django models. That was convenient for proving the generator could work, but it also meant the default behavior included models the developer might never have intended to expose through an API.

I eventually reversed that assumption. Model participation should be explicit, not automatic. A developer should decide which models belong in the generated API surface.

Knit now provides decorator-based and settings-based registration through a shared registry. Settings-based registration also permits models from Django or third-party applications to participate without modifying their source code.

This adds a configuration step, but makes the scope of generation intentional and easier to reason about.

Generation pipeline

Django Knit begins with explicitly registered Django models. Their configuration and model metadata are collected before being passed to the generation templates.

  1. 01
    Registration

    Models opt in through the @knit_model decorator or Django settings.

  2. 02
    Registry

    Associates models with their developer-supplied configuration.

  3. 03
    BuildHelper / ModelData

    Inspects Django models and assembles metadata for downstream generators.

  4. 04
    Code generation

    Templates produce Django REST Framework and TypeScript source files.

Simplified build pipeline. The shared metadata contract is still under active development.

Developer configuration

Registration is explicit. Models can be registered through a decorator or application settings, including models owned by third-party Django applications.

KNIT["MODELS_TO_REGISTER"] = {
            "auth.Group": {
                "searchable_fields": ("name",),
            },
            "contenttypes.ContentType": {},
        }

Settings-based registration allows developers to opt into generation without modifying the underlying model definitions.

API composition

My preference is to keep resources independently accessible, return lightweight relationship references, and provide shallow nested representations when a particular view benefits from additional context.

The goal isn't to minimize requests or response sizes at all costs. It's to balance database work, serialization, payload size, and network overhead without forcing every consumer to retrieve an entire object graph.

This approach relies on flexible filtering and efficient collection retrieval. Where ordinary resource endpoints aren't sufficient, targeted actions or specialized endpoints can serve more demanding access patterns.

Knit's simple and detailed generated types reflect this approach. Completing and validating the frontend integration remains part of the ongoing development work.

Configuration versus metadata

As the generators grew more capable, configuration supplied by developers became increasingly mixed with information discovered from Django models.

The current refactor separates these responsibilities. KnitModelConfig describes developer intent, while BuildHelper collects model and field information into structures such as ModelData and FieldData for downstream generation.

The benefit is a clearer boundary between what a developer requests and what the generator discovers. It also gives the Python and TypeScript generators a common source of model information.

This separation is still evolving. The metadata structures retain Django-specific objects, and some templates have yet to adopt the newer configuration model consistently.

Current state

Django Knit remains an active prototype. Explicit registration and model metadata collection are implemented, and existing generations demonstrate Python serializers, viewsets, TypeScript models, and API clients.

Current work focuses on reconciling the generation templates with the newer registration and configuration architecture. A separate project's frontend API design provides the intended direction for completing the TypeScript integration, but that integration has not yet been completed and verified end to end.