Skip to main content

Overview

milvusql-django is a Django database backend built on the milvusql DBAPI.

Configuration​

DATABASES = {
"default": {
"ENGINE": "milvusql_django",
"NAME": "/path/to/items.db", # Milvus Lite: a local file
}
}

Or a real server, using Django's normal HOST/PORT/USER/PASSWORD settings:

DATABASES = {
"default": {
"ENGINE": "milvusql_django",
"NAME": "default", # Milvus db_name
"HOST": "localhost",
"PORT": 19530,
"USER": "root",
"PASSWORD": "Milvus",
}
}

USER and PASSWORD are reassembled into Milvus's token parameter (itself a "user:password" pair) rather than PASSWORD alone being forwarded and USER silently dropped.

What goes through Django's normal compiler, unmodified​

Standard Model/Field CRUD and scalar filtering — everything that's genuinely SQL-shaped:

from django.db import models

class Item(models.Model):
category = models.CharField(max_length=64)

Item.objects.create(category="book")
Item.objects.filter(category="book")
Item.objects.filter(category="book").delete()

What doesn't — and why​

Django's SQLCompiler has no concept of an ANN ORDER BY or a HYBRID SEARCH clause. Teaching it one is the path django-cassandra-engine took — forking Django's Model metaclass to fake Field/Options on top of a completely different query model — and this package deliberately didn't. Milvus is a better fit for real Django integration than Cassandra is (everything except vector search really is SQL-shaped here), so the fork was avoidable: vector and hybrid search go through explicit helper functions instead, building MilvusQL text directly and executing it through connection.cursor(). See Search Helpers.

JOINs, .annotate() grouping and correlated Exists() do work​

The relational constructs Django's compiler itself renders as JOIN/GROUP BY/HAVING/subquery SQL are a different story from vector search — that SQL reaches milvusql's DBAPI-level relational engine (see MilvusQL Concepts), which plans it into one Milvus read per collection and evaluates the rest client-side with Polars. No backend-side change was needed for this to work:

Item.objects.filter(category__title="book")               # relation lookup -> INNER JOIN
Item.objects.values("category_id").annotate(n=Count("id")).filter(n__gt=1) # GROUP BY + HAVING
Item.objects.filter(Exists(Category.objects.filter(pk=OuterRef("category_id")))) # correlated EXISTS

Django groups and orders by ordinal position (GROUP BY 1, ORDER BY 2 DESC) rather than by name, and quotes every identifier — the planner resolves both. Exists(... OuterRef(...)) is decorrelated into a semi join (~Exists(...) into an anti join); a correlated value per outer row (annotate(Subquery(... OuterRef(...)))) is rejected by name instead, since a semi-join rewrite can't express it.

Scope​

Package pieceStatus
Model/Field CRUD, .filter()Works, through Django's normal compiler
VectorFieldWorks — see Vector Field
TextFieldWorks — Milvus's analyzer-enabled full-text input, TEXT in DDL (was VARCHAR(65535) before v1.0.0 — see Schema & Migrations)
Relation .filter(), .values().annotate(...), correlated Exists(... OuterRef(...))Works, through the DBAPI's relational engine — see above
schema_editor().create_model()Works — see Schema & Migrations
RemoveField/AlterFieldRaise NotImplementedError on purpose — Milvus can't do either
vector_search()/hybrid_search() helpersWorks — see Search Helpers
dbshellNot supported — Milvus Lite has no CLI client