Skip to main content

django-milvus

Django integration for Milvus vector database. Use Milvus as a Django secondary database with a familiar ORM-like interface for storing and searching vector embeddings.

PyPI version Django

Features

  • Django ORM-like interface — Define Milvus collections as Python model classes with typed fields
  • Vector similarity search — Search by embedding vectors with COSINE, L2, or IP metrics
  • All Milvus field types — FloatVector, BinaryVector, SparseVector, VarChar, JSON, Array, scalar types
  • All index types — HNSW, IVF_FLAT, IVF_PQ, DISKANN, AUTOINDEX, and more
  • Django-style filtering — filter(score__gt=0.5), filter(category__in=[...]), exclude(...)
  • Chainable QuerySet — .filter().limit().only().search() with lazy evaluation
  • Bulk operations — bulk_create(), upsert() with automatic batching
  • Partition support — Create and query specific partitions
  • Collection management — Management commands for creating, inspecting, and dropping collections
  • Alias & RBAC management — Full support for Milvus aliases, users, roles, and privileges
  • Django settings integration — Configure connections via DATABASES or MILVUS settings
  • Database router — Routes MilvusModel operations to the correct backend
  • Interactive shell — milvus_shell management command with connected client

Installation

pip install django-milvus

Requirements

  • Python >= 3.9
  • Django >= 4.2
  • pymilvus >= 2.4.0
  • A running Milvus instance (2.4.x recommended)

Quick Start

1. Configure Django Settings

# settings.py
INSTALLED_APPS = [
    # ... your apps
    'django_milvus',
]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    },
    'milvus': {
        'ENGINE': 'django_milvus.backend',
        'HOST': 'localhost',
        'PORT': 19530,
        'USER': '',           # optional
        'PASSWORD': '',       # optional
        'NAME': 'default',    # Milvus database name
    },
}

DATABASE_ROUTERS = ['django_milvus.routers.MilvusRouter']

Or use the MILVUS setting for more control:

MILVUS = {
    'default': {
        'URI': 'http://localhost:19530',
        'TOKEN': 'root:Milvus',
        'DB_NAME': 'default',
        'TIMEOUT': 30,
    },
}

2. Define Models

# myapp/models.py
from django_milvus.models import MilvusModel
from django_milvus.fields import (
    PrimaryKeyField, VarCharField, FloatVectorField,
    Int64Field, FloatField, BoolField, JSONField,
)
from django_milvus.indexes import HNSW, InvertedIndex


class Document(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    title = VarCharField(max_length=512)
    content = VarCharField(max_length=8192)
    embedding = FloatVectorField(dim=768)
    category = VarCharField(max_length=64)
    score = FloatField(default=0.0)
    is_published = BoolField(default=True)
    metadata = JSONField(default=dict)

    class MilvusMeta:
        collection_name = 'documents'
        database = 'milvus'                 # matches DATABASES key
        description = 'Document embeddings'
        consistency_level = 'Bounded'
        enable_dynamic_field = False

    class MilvusIndexes:
        embedding_idx = HNSW(
            field='embedding',
            metric_type='COSINE',
            M=16,
            efConstruction=256,
        )
        category_idx = InvertedIndex(field='category')

3. Create the Collection

python manage.py milvus_sync

Or programmatically:

Document.create_collection()          # Create if not exists
Document.create_collection(drop_existing=True)  # Recreate

4. Insert Data

# Single insert
doc = Document(
    title="Introduction to AI",
    content="Artificial intelligence is...",
    embedding=[0.1, 0.2, ...],  # 768-dim vector
    category="tech",
    score=0.95,
)
doc.save()
print(doc.pk)  # Auto-generated ID

# Bulk insert
docs = [
    Document(title="Doc 1", content="...", embedding=[...], category="tech"),
    Document(title="Doc 2", content="...", embedding=[...], category="science"),
]
Document.objects.bulk_create(instances=docs)

# Insert from raw dicts
Document.objects.bulk_create(data=[
    {"title": "Doc 3", "content": "...", "embedding": [...], "category": "tech"},
])

# Create shortcut
doc = Document.objects.create(
    title="Quick doc",
    content="...",
    embedding=[...],
    category="tech",
)

5. Vector Similarity Search

query_vector = get_embedding("What is machine learning?")  # Your embedding function

# Basic search
results = Document.objects.search(
    vector=query_vector,
    vector_field='embedding',
    limit=10,
    metric_type='COSINE',
)

for result in results:
    print(f"{result.entity.title} (distance: {result.distance})")

# Search with filters
results = Document.objects.filter(
    category='tech',
    is_published=True,
).search(
    vector=query_vector,
    limit=5,
)

# Search with specific output fields
results = Document.objects.search(
    vector=query_vector,
    limit=20,
    output_fields=['title', 'category', 'score'],
)

# Search with custom parameters
results = Document.objects.search(
    vector=query_vector,
    limit=10,
    search_params={"ef": 128},  # HNSW search parameter
)

# Auto-detect vector field (works when model has single vector field)
results = Document.objects.search(vector=query_vector, limit=10)

6. Query and Filter

# Get all (with limit)
docs = Document.objects.all().limit(100)

# Filter with Django-style lookups
docs = Document.objects.filter(category='tech')
docs = Document.objects.filter(score__gt=0.5)
docs = Document.objects.filter(score__gte=0.5, score__lte=1.0)
docs = Document.objects.filter(category__in=['tech', 'science'])
docs = Document.objects.filter(title__like='AI%')

# Raw Milvus filter expressions
docs = Document.objects.filter(expr='category == "tech" and score > 0.5')

# Chainable operations
docs = (
    Document.objects
    .filter(category='tech')
    .filter(is_published=True)
    .limit(50)
    .only('title', 'score')
)

# Exclude
docs = Document.objects.exclude(category='spam')

# Get single object
doc = Document.objects.get(id=42)
doc = Document.objects.get_or_none(id=999)
first_doc = Document.objects.filter(category='tech').first()

# Count and exists
count = Document.objects.filter(category='tech').count()
has_docs = Document.objects.filter(score__gt=0.9).exists()

7. Update and Delete

# Update single instance
doc = Document.objects.get(id=42)
doc.title = "Updated Title"
doc.save()  # Upserts

# Upsert multiple
Document.objects.upsert(instances=[doc1, doc2, doc3])

# Delete by filter
Document.objects.delete(category='spam')
Document.objects.filter(score__lt=0.1).delete()

# Delete by IDs
Document.objects.delete_by_ids([1, 2, 3])

# Delete single instance
doc.delete()

8. Partitions

from django_milvus import schema

# Create partitions
schema.create_partition('documents', 'tech_docs')
schema.create_partition('documents', 'science_docs')

# Query specific partitions
results = (
    Document.objects
    .using_partitions('tech_docs')
    .filter(score__gt=0.5)
    .limit(10)
)

# Search within partitions
results = (
    Document.objects
    .using_partitions('tech_docs')
    .search(vector=query_vector, limit=10)
)

# List/manage partitions
schema.list_partitions('documents')
schema.has_partition('documents', 'tech_docs')
schema.drop_partition('documents', 'tech_docs')

Complete Field Reference

Field Milvus Type Notes
PrimaryKeyField INT64 or VARCHAR auto_id=True for auto-increment
Int64PrimaryKey INT64 Shortcut for INT64 PK
VarCharPrimaryKey VARCHAR Shortcut for VARCHAR PK
BoolField BOOL Boolean values
Int8Field INT8 -128 to 127
Int16Field INT16 -32768 to 32767
Int32Field INT32 32-bit integer
Int64Field INT64 64-bit integer
FloatField FLOAT 32-bit float
DoubleField DOUBLE 64-bit float
VarCharField VARCHAR max_length required
JSONField JSON Dict/list data
ArrayField ARRAY element_type, max_capacity
FloatVectorField FLOAT_VECTOR dim required
BinaryVectorField BINARY_VECTOR dim (multiple of 8)
Float16VectorField FLOAT16_VECTOR dim required
BFloat16VectorField BFLOAT16_VECTOR dim required
SparseFloatVectorField SPARSE_FLOAT_VECTOR Sparse embeddings

Aliases: VectorField = FloatVectorField, CharField = VarCharField, IntegerField = Int64Field

Complete Index Reference

Index Type Best For
FLAT Exact Small datasets, perfect accuracy
IVF_FLAT Approximate Good balance, nlist param
IVF_SQ8 Approximate Lower memory, nlist param
IVF_PQ Approximate Large datasets, nlist, m, nbits
HNSW Graph-based Best speed/accuracy, M, efConstruction
SCANN Approximate Fast, nlist param
DISKANN Disk-based Very large datasets
AUTOINDEX Auto Let Milvus choose
BIN_FLAT Binary Binary vectors
BIN_IVF_FLAT Binary Binary vectors, nlist
SPARSE_INVERTED_INDEX Sparse Sparse vectors
SPARSE_WAND Sparse Sparse vectors
ScalarIndex Scalar Filter acceleration
TrieIndex Scalar VARCHAR prefix queries
InvertedIndex Scalar General-purpose filtering

Index Examples

All index classes are imported from django_milvus.indexes. Define them inside a MilvusIndexes inner class on your model, or create them programmatically via schema.create_index().

FLAT — Brute-Force (small datasets, 100% recall)

from django_milvus.models import MilvusModel
from django_milvus.fields import PrimaryKeyField, VarCharField, FloatVectorField
from django_milvus.indexes import FLAT

class SmallCollection(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    text = VarCharField(max_length=512)
    embedding = FloatVectorField(dim=128)

    class MilvusMeta:
        collection_name = 'small_collection'

    class MilvusIndexes:
        embedding_idx = FLAT(field='embedding', metric_type='L2')

HNSW — Graph-Based (best speed/accuracy trade-off)

from django_milvus.indexes import HNSW

class ArticleEmbedding(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    title = VarCharField(max_length=256)
    embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'articles'

    class MilvusIndexes:
        # M: max connections per node (higher = better recall, more memory)
        # efConstruction: search breadth during build (higher = better quality)
        embedding_idx = HNSW(
            field='embedding',
            metric_type='COSINE',
            M=16,
            efConstruction=256,
        )

IVF_FLAT — Inverted File Index (balanced for medium datasets)

from django_milvus.indexes import IVF_FLAT

class ProductEmbedding(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    name = VarCharField(max_length=256)
    embedding = FloatVectorField(dim=512)

    class MilvusMeta:
        collection_name = 'products'

    class MilvusIndexes:
        # nlist: number of clusters (higher = faster search, lower recall)
        embedding_idx = IVF_FLAT(
            field='embedding',
            metric_type='IP',
            nlist=256,
        )

IVF_PQ — Product Quantization (large datasets, lower memory)

from django_milvus.indexes import IVF_PQ

class LargeScaleDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'large_docs'

    class MilvusIndexes:
        # m: sub-vector count (must divide dim evenly)
        # nbits: quantization bits per sub-vector
        embedding_idx = IVF_PQ(
            field='embedding',
            metric_type='L2',
            nlist=128,
            m=24,        # 768 / 24 = 32-dim sub-vectors
            nbits=8,
        )

IVF_SQ8 — Scalar Quantization (lower memory than IVF_FLAT)

from django_milvus.indexes import IVF_SQ8

class CompressedDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=384)

    class MilvusMeta:
        collection_name = 'compressed_docs'

    class MilvusIndexes:
        embedding_idx = IVF_SQ8(
            field='embedding',
            metric_type='COSINE',
            nlist=128,
        )

SCANN — Scalable Nearest Neighbors (fast approximate search)

from django_milvus.indexes import SCANN

class FastSearchDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=256)

    class MilvusMeta:
        collection_name = 'fast_search'

    class MilvusIndexes:
        embedding_idx = SCANN(
            field='embedding',
            metric_type='COSINE',
            nlist=128,
        )

DISKANN — Disk-Based Index (billion-scale datasets)

from django_milvus.indexes import DISKANN

class HugeCollection(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'huge_collection'

    class MilvusIndexes:
        # No extra params needed — data is indexed on disk
        embedding_idx = DISKANN(field='embedding', metric_type='L2')

AUTOINDEX — Let Milvus Choose

from django_milvus.indexes import AUTOINDEX

class AutoDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'auto_docs'

    class MilvusIndexes:
        embedding_idx = AUTOINDEX(field='embedding', metric_type='COSINE')

BIN_FLAT / BIN_IVF_FLAT — Binary Vector Indexes

from django_milvus.fields import BinaryVectorField
from django_milvus.indexes import BIN_FLAT, BIN_IVF_FLAT

class BinaryHashModel(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    hash_vector = BinaryVectorField(dim=256)  # must be multiple of 8

    class MilvusMeta:
        collection_name = 'binary_hashes'

    class MilvusIndexes:
        # Use HAMMING or JACCARD metric for binary vectors
        hash_idx = BIN_FLAT(field='hash_vector', metric_type='HAMMING')

        # Or with clustering for larger datasets:
        # hash_idx = BIN_IVF_FLAT(
        #     field='hash_vector', metric_type='JACCARD', nlist=64
        # )

SPARSE_INVERTED_INDEX / SPARSE_WAND — Sparse Vector Indexes

from django_milvus.fields import SparseFloatVectorField
from django_milvus.indexes import SPARSE_INVERTED_INDEX, SPARSE_WAND

class SparseEmbeddingModel(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    title = VarCharField(max_length=256)
    sparse_embedding = SparseFloatVectorField()

    class MilvusMeta:
        collection_name = 'sparse_docs'

    class MilvusIndexes:
        # drop_ratio_build: fraction of small values to discard (saves space)
        sparse_idx = SPARSE_INVERTED_INDEX(
            field='sparse_embedding',
            metric_type='IP',
            drop_ratio_build=0.2,
        )

        # Alternative: SPARSE_WAND is faster for top-k retrieval
        # sparse_idx = SPARSE_WAND(
        #     field='sparse_embedding', metric_type='IP', drop_ratio_build=0.2
        # )

Scalar Indexes — Speed Up Filtering

from django_milvus.fields import Int64Field, BoolField
from django_milvus.indexes import InvertedIndex, TrieIndex, ScalarIndex

class FilterableDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    category = VarCharField(max_length=64)
    author = VarCharField(max_length=128)
    view_count = Int64Field()
    is_published = BoolField(default=True)
    embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'filterable_docs'

    class MilvusIndexes:
        # HNSW for vector search
        emb_idx = HNSW(field='embedding', metric_type='COSINE')

        # InvertedIndex — best general-purpose scalar index
        category_idx = InvertedIndex(field='category')

        # TrieIndex — optimized for VARCHAR prefix queries (like "AI%")
        author_idx = TrieIndex(field='author')

        # STL_SORT — good for numeric range queries
        views_idx = ScalarIndex(field='view_count', index_type='STL_SORT')

Programmatic Index Creation (without MilvusIndexes)

from django_milvus import schema

# Create an HNSW index on an existing collection
schema.create_index(
    'documents',
    'embedding',
    index_type='HNSW',
    metric_type='COSINE',
    params={'M': 16, 'efConstruction': 256},
)

# Create a scalar index
schema.create_index(
    'documents',
    'category',
    index_type='INVERTED',
    metric_type='',
)

# List, inspect, and drop indexes
schema.list_indexes('documents')
schema.describe_index('documents', 'embedding')
schema.drop_index('documents', 'embedding')

Multiple Indexes on One Model (Multi-Vector + Scalar)

class HybridSearchDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    title = VarCharField(max_length=256)
    category = VarCharField(max_length=64)
    dense_embedding = FloatVectorField(dim=768)
    sparse_embedding = SparseFloatVectorField()

    class MilvusMeta:
        collection_name = 'hybrid_docs'

    class MilvusIndexes:
        dense_idx = HNSW(field='dense_embedding', metric_type='COSINE', M=32)
        sparse_idx = SPARSE_INVERTED_INDEX(field='sparse_embedding', metric_type='IP')
        category_idx = InvertedIndex(field='category')

# Search dense vectors with scalar filter
results = HybridSearchDoc.objects.filter(
    category='tech',
).search(
    vector=dense_query,
    vector_field='dense_embedding',
    limit=10,
)

Filter Lookups

Lookup Milvus Expression
field=value field == value
field__eq=value field == value
field__ne=value field != value
field__gt=value field > value
field__gte=value field >= value
field__lt=value field < value
field__lte=value field <= value
field__in=[...] field in [...]
field__nin=[...] field not in [...]
field__like="pat" field like "pat"
field__exists=True exists field
field__json_contains=v json_contains(field, v)
field__array_contains=v array_contains(field, v)

Schema Management Functions

from django_milvus import schema

# Collections
schema.list_collections()
schema.describe_collection('documents')
schema.has_collection('documents')
schema.rename_collection('old_name', 'new_name')
schema.get_collection_stats('documents')
schema.load_collection('documents')
schema.release_collection('documents')
schema.get_load_state('documents')
schema.drop_collection('documents')

# Indexes
schema.create_index('documents', 'embedding', index_type='HNSW',
                    metric_type='COSINE', params={'M': 16})
schema.list_indexes('documents')
schema.describe_index('documents', 'index_name')
schema.drop_index('documents', 'index_name')

# Partitions
schema.create_partition('documents', 'partition_a')
schema.drop_partition('documents', 'partition_a')
schema.has_partition('documents', 'partition_a')
schema.list_partitions('documents')
schema.load_partitions('documents', ['partition_a'])
schema.release_partitions('documents', ['partition_a'])

# Aliases
schema.create_alias('documents', 'docs_alias')
schema.drop_alias('docs_alias')
schema.alter_alias('documents_v2', 'docs_alias')
schema.describe_alias('docs_alias')
schema.list_aliases('documents')

# User & RBAC
schema.create_user('alice', 'password123')
schema.drop_user('alice')
schema.update_password('alice', 'old_pass', 'new_pass')
schema.list_users()
schema.describe_user('alice')
schema.create_role('reader')
schema.drop_role('reader')
schema.list_roles()
schema.grant_role('alice', 'reader')
schema.revoke_role('alice', 'reader')
schema.grant_privilege('reader', 'Collection', 'documents', 'Search')
schema.revoke_privilege('reader', 'Collection', 'documents', 'Search')

Management Commands

# Create collections for all MilvusModel classes
python manage.py milvus_sync
python manage.py milvus_sync --drop-existing
python manage.py milvus_sync --models myapp.models.Document

# Show collection statistics
python manage.py milvus_stats
python manage.py milvus_stats --collection documents --verbose

# Drop collections
python manage.py milvus_drop --collection documents
python manage.py milvus_drop --all --yes

# Interactive shell
python manage.py milvus_shell

Advanced Usage

Direct Client Access

# Access the underlying pymilvus MilvusClient
client = Document.objects.get_client()
client.list_collections()

# From connection manager
from django_milvus.connection import get_milvus_client
client = get_milvus_client('milvus')

Raw Operations

# Raw query with Milvus expressions
results = Document.objects.query_raw(
    filter_expr='category == "tech" and score > 0.5',
    output_fields=['title', 'score'],
    limit=100,
)

# Raw search
results = Document.objects.search_raw(
    data=[[0.1, 0.2, ...]],
    anns_field='embedding',
    limit=10,
    search_params={"metric_type": "COSINE", "params": {"ef": 128}},
    filter_expr='is_published == true',
)

# Raw insert
Document.objects.insert_raw([
    {"title": "Doc", "embedding": [...], "category": "tech"},
])

Multiple Vector Fields

class MultiVectorDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    title = VarCharField(max_length=256)
    title_embedding = FloatVectorField(dim=384)
    content_embedding = FloatVectorField(dim=768)

    class MilvusMeta:
        collection_name = 'multi_vector_docs'

    class MilvusIndexes:
        title_idx = HNSW(field='title_embedding', metric_type='COSINE')
        content_idx = HNSW(field='content_embedding', metric_type='COSINE')

# Search specific vector field
results = MultiVectorDoc.objects.search(
    vector=title_query,
    vector_field='title_embedding',
    limit=10,
)

Consistency Levels

# Strong consistency (reads reflect latest writes)
results = Document.objects.consistency('Strong').filter(category='tech')

# Session consistency
results = Document.objects.consistency('Session').search(vector=v, limit=10)

Dynamic Fields

class FlexDoc(MilvusModel):
    id = PrimaryKeyField(auto_id=True)
    embedding = FloatVectorField(dim=128)

    class MilvusMeta:
        collection_name = 'flex_docs'
        enable_dynamic_field = True

# Store arbitrary fields
doc = FlexDoc(embedding=[...], custom_field="value", tags=["a", "b"])
doc.save()

License

MIT License. See LICENSE for details.

Release files for django-milvus 0.1.11

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

Source distribution (sdist)

Source distribution for django-milvus 0.1.11
File Size Uploaded
django_milvus-0.1.11.tar.gz 45.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for django-milvus 0.1.11
File Interpreter ABI Platform
django_milvus-0.1.11-py3-none-any.whl Python 3 none any Details

Total release size: 87.8 kB

Release files / django_milvus-0.1.11.tar.gz

Download URL django_milvus-0.1.11.tar.gz
Size 45.2 kB
Tags Source
SHA-256 checksum
How to use checksums
dce8a2b58d422db5b8228aa20dbbf180584a4d2101c16004dc44d48da534a4da
BLAKE2b-256 checksum
How to use checksums
01d6e04be0390cb5536a82b6d63d80bf15af26774ada1b43adc0b07e00bd156b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Release files / django_milvus-0.1.11-py3-none-any.whl

Download URL django_milvus-0.1.11-py3-none-any.whl
Size 42.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c50c31ca78b6ab6a3255bcc71e0eb8af996370023bc86bb4d6d6358f9567de7f
BLAKE2b-256 checksum
How to use checksums
2cec45db94c2147aed19271eb2e512c576ad8df6c9b888670ad39046f0d367e6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.2

Release history Release notifications | RSS feed

This release

0.1.11 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

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