Skip to content

Release Notes

VersionDateSummary
v2.9.0
diff
2026-01-14notes
Release notes

Release Notes

[v2.9.0] - 2026-01-14


✨ Added

  • Detail-Specific Query Optimizations:
  • New QuerySet.detail configuration for detail-specific select_related and prefetch_related
  • New serializable_detail_fields property on ModelUtil for accessing detail-specific fields
  • New _get_serializable_field_names() helper method for DRY field retrieval
  • New DETAIL scope added to QueryUtilBaseScopesSchema

  • Fallback Mechanism for Detail Schema:

  • generate_detail_s() now falls back to read schema when no DetailSerializer is defined
  • get_fields("detail") falls back to read fields when no detail fields are declared
  • _get_read_optimizations("detail") falls back to QuerySet.read when QuerySet.detail is not defined

🛠 Changed

  • API Parameter Change: is_for_readis_for:
  • Renamed is_for_read: bool parameter to is_for: Literal["read", "detail"] | None across all ModelUtil methods:
    • get_objects()
    • get_object()
    • read_s()
    • list_read_s()
    • _get_base_queryset()
    • _apply_query_optimizations()
    • _serialize_queryset()
    • _serialize_single_object()
    • _handle_query_mode()
    • _read_s()
  • This enables explicit control over which optimization strategy to use

  • Query Optimization Methods Now Accept is_for Parameter:

  • get_select_relateds(is_for: Literal["read", "detail"] = "read")
  • get_reverse_relations(is_for: Literal["read", "detail"] = "read")
  • _get_read_optimizations(is_for: Literal["read", "detail"] = "read")

  • APIViewSet Retrieve Endpoint:

  • Now uses is_for="detail" when schema_detail is available
  • Falls back to is_for="read" when no detail schema is configured

  • Code Formatting Improvements:

  • Reformatted multi-line tuples in _is_reverse_relation()
  • Reformatted conditional in _warn_missing_relation_serializer()
  • Reformatted error message in get_schema_out_data()

🐛 Fixed

  • Query Optimization Fallback Bug:
  • Fixed _get_read_optimizations() to fall back to read config when detail config is not defined
  • Previously returned empty ModelQuerySetSchema() when QuerySet.detail was missing, losing all optimizations

📝 Documentation

  • ModelUtil Documentation (docs/api/models/model_util.md):
  • Updated all method signatures from is_for_read: bool to is_for: Literal["read", "detail"] | None
  • Added QuerySet.detail configuration example
  • Added serializable_detail_fields property documentation
  • Updated examples to show is_for="read" and is_for="detail" usage
  • Added fallback behavior notes for detail optimizations

  • ModelSerializer Documentation (docs/api/models/model_serializer.md):

  • Added Fallback Behavior note in DetailSerializer section
  • Updated generate_detail_s() comment to indicate fallback to read schema
  • Updated fields table to mention fallback behavior

  • Serializer Documentation (docs/api/models/serializers.md):

  • Added QuerySet.detail configuration example
  • Added explanation of how each QuerySet config is applied (read, detail, queryset_request, extras)

🧪 Tests

  • Updated Test Cases:
  • Updated all is_for_read=True to is_for="read" across test files
  • Updated all is_for_read=False to is_for=None across test files
  • Renamed test_generate_detail_schema_returns_none_when_not_configured to test_generate_detail_schema_falls_back_to_read_when_not_configured
  • Updated test_fallback_to_schema_out_when_no_detail to test_detail_schema_falls_back_to_read_schema

  • New Test Cases:

  • DetailFieldsModelSerializer - Test model with different read vs detail fields including a relation
  • ModelUtilIsForDetailTestCase - Tests for is_for='detail' parameter:
    • test_serializable_fields_returns_read_fields()
    • test_serializable_detail_fields_returns_detail_fields()
    • test_get_select_relateds_read_no_relations()
    • test_get_select_relateds_detail_includes_relation()
    • test_apply_query_optimizations_read_vs_detail()
    • test_get_serializable_field_names_read()
    • test_get_serializable_field_names_detail()
  • ReadOnlyQuerySetModelSerializer - Test model with QuerySet.read but no QuerySet.detail
  • ModelUtilOptimizationFallbackTestCase - Tests for optimization fallback behavior:
    • test_get_read_optimizations_read()
    • test_get_read_optimizations_detail_falls_back_to_read()
    • test_apply_query_optimizations_detail_uses_read_fallback()

🔧 Internal Changes

  • BaseSerializer Changes:
  • Added detail = ModelQuerySetSchema() to inner QuerySet class
  • Added fallback logic in get_fields() for detail type

  • QueryUtilBaseScopesSchema Changes:

  • Added DETAIL: str = "detail" scope constant

  • QueryUtil Changes:

  • Added detail_config property for accessing detail query configuration

🚀 Use Cases & Examples

Detail-Specific Query Optimizations

```python from ninja_aio.models import ModelSerializer from ninja_aio.schemas.helpers import ModelQuerySetSchema

class Article(ModelSerializer): title = models.CharField(max_length=200) summary = models.TextField() content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag) comments = models.ManyToManyField(Comment)

class ReadSerializer:
    # List view: minimal fields
    fields = ["id", "title", "summary", "author"]

class DetailSerializer:
    # Detail view: all fields including expensive relations
    fields = ["id", "title", "summary", "content", "author", "tags", "comments"]

class QuerySet:
    # Optimizations for list endpoint
    read = ModelQuerySetSchema(
        select_related=["author"],
        prefetch_related=[],
    )
    # Optimizations for retrieve endpoint (more aggressive prefetching)
    detail = ModelQuerySetSchema(
        select_related=["author", "author__profile"],
        prefetch_related=["tags", "comments", "comments__author"],
    )

```

Behavior: - GET /articles/ uses QuerySet.read optimizations (light prefetching) - GET /articles/{pk} uses QuerySet.detail optimizations (full prefetching)

Fallback Behavior

```python class Article(ModelSerializer): class ReadSerializer: fields = ["id", "title", "content"]

class QuerySet:
    read = ModelQuerySetSchema(
        select_related=["author"],
        prefetch_related=["tags"],
    )
    # No detail config - will fall back to read!

Both list and retrieve use QuerySet.read optimizations

generate_detail_s() returns same schema as generate_read_s()

```

Using is_for Parameter Directly

```python from ninja_aio.models import ModelUtil

util = ModelUtil(Article)

For list operations

qs = await util.get_objects(request, is_for="read")

For single object retrieval

obj = await util.get_object(request, pk=1, is_for="detail")

For serialization

data = await util.read_s(schema, request, instance=obj, is_for="detail") items = await util.list_read_s(schema, request, instances=qs, is_for="read") ```


🔍 Migration Guide

Breaking Change: is_for_readis_for

If you call ModelUtil methods directly with is_for_read, update to use is_for:

```python

Before (v2.8.0)

await util.get_objects(request, is_for_read=True) await util.get_object(request, pk=1, is_for_read=True) await util.read_s(schema, request, instance=obj, is_for_read=True)

After (v2.9.0)

await util.get_objects(request, is_for="read") await util.get_object(request, pk=1, is_for="detail") await util.read_s(schema, request, instance=obj, is_for="detail") ```

Mapping: | Old Parameter | New Parameter | |---------------|---------------| | is_for_read=True | is_for="read" (for list) or is_for="detail" (for retrieve) | | is_for_read=False | is_for=None |

Adding Detail-Specific Optimizations

```python

Before (v2.8.0) - Same optimizations for list and retrieve

class QuerySet: read = ModelQuerySetSchema( select_related=["author"], prefetch_related=["tags", "comments"], # Always loaded! )

After (v2.9.0) - Different optimizations per operation

class QuerySet: read = ModelQuerySetSchema( select_related=["author"], prefetch_related=[], # Light for list ) detail = ModelQuerySetSchema( select_related=["author", "author__profile"], prefetch_related=["tags", "comments"], # Full for retrieve ) ```


📊 Performance Benefits

| Scenario | Without Detail Config | With Detail Config | |----------|----------------------|-------------------| | List 100 articles | Prefetches tags + comments for all | Only prefetches what's needed for list | | Retrieve single | Uses list optimizations | Uses detail-specific optimizations | | N+1 queries | May occur if list over-fetches | Optimized per endpoint | | Memory usage | Higher (unnecessary prefetch) | Optimized per operation |


⚠️ Important Notes

  • Breaking Change: is_for_read: bool parameter renamed to is_for: Literal["read", "detail"] | None
  • Fallback Behavior: All fallbacks are automatic - no configuration needed for backward compatibility
  • QuerySet.detail: Optional - falls back to QuerySet.read if not defined
  • DetailSerializer fields: Optional - falls back to ReadSerializer fields if not defined
  • generate_detail_s(): Now always returns a schema (falls back to read schema)

🔗 Links


Version History

For older versions, please refer to the GitHub releases page.

v2.8.0
diff
2026-01-14notes
Release notes

Release Notes

[v2.8.0] - 2026-01-14


✨ Added

  • Detail Schema Support for Retrieve Endpoints:
  • New DetailSerializer configuration class for ModelSerializer
  • New schema_detail configuration option for Serializer Meta class
  • New schema_detail attribute on APIViewSet for custom detail schemas
  • New generate_detail_s() method for generating detail schemas
  • Retrieve endpoint (GET /{base}/{pk}) now uses schema_detail when available, falling back to schema_out
  • Enables performance optimization: minimal fields for list views, full details for single object retrieval

  • serializer_class Support for M2MRelationSchema:

  • M2MRelationSchema now accepts serializer_class parameter for plain Django models
  • Auto-generates related_schema from the serializer when provided
  • Alternative to manually providing related_schema for plain models
  • Validation ensures serializer_class cannot be used when model is already a ModelSerializer

🛠 Changed

  • APIViewSet Schema Generation:
  • get_schemas() now returns a 4-tuple: (schema_out, schema_detail, schema_in, schema_update)
  • New _get_retrieve_schema() helper method for retrieve endpoint schema selection
  • retrieve_view() updated to use detail schema when available

  • Refactored get_schema_out_data() Function:

  • Extracted helper methods for better code organization:
    • _is_reverse_relation() - Check if field is a reverse relation
    • _is_forward_relation() - Check if field is a forward relation
    • _warn_missing_relation_serializer() - Emit warning for missing serializer mappings
    • _process_field() - Process single field and determine classification
  • Renamed parameter type to schema_type to avoid shadowing built-in
  • Renamed internal variable rels to forward_rels for clarity
  • Now accepts schema_type: Literal["Out", "Detail"] parameter

  • Performance Optimization in _generate_union_schema():

  • Fixed double method call issue using walrus operator
  • generate_related_s() now called once per serializer instead of twice

  • Updated Type Definitions:

  • S_TYPES now includes "detail": Literal["read", "detail", "create", "update"]
  • SCHEMA_TYPES now includes "Detail": Literal["In", "Out", "Detail", "Patch", "Related"]

📝 Documentation

  • ModelSerializer Documentation (docs/api/models/model_serializer.md):
  • New DetailSerializer section with complete documentation
  • Updated schema generation table to include generate_detail_s()
  • Added example showing List vs Detail output differences
  • Updated "Auto-Generated Schemas" to show five schema types

  • Serializer Documentation (docs/api/models/serializers.md):

  • Added schema_detail to Meta configuration options
  • New "Detail Schema for Retrieve Endpoint" section
  • Updated schema generation examples to include generate_detail_s()

  • APIViewSet Documentation (docs/api/views/api_view_set.md):

  • Updated CRUD endpoints table to show retrieve uses schema_detail
  • Added schema_detail to Core Attributes table
  • New "Detail Schema for Retrieve Endpoint" section with examples
  • Updated automatic schema generation section
  • Added serializer_class documentation for M2MRelationSchema
  • Added tabbed examples for related_schema vs serializer_class usage

🧪 Tests

  • New Detail Schema Test Cases:
  • DetailSerializerTestCase in tests/test_serializers.py:

    • test_generate_detail_schema_with_serializer() - Basic detail schema generation
    • test_generate_detail_schema_returns_none_when_not_configured() - None when not configured
    • test_detail_schema_with_relations() - Relations in detail schema
    • test_detail_schema_with_custom_fields() - Custom fields support
    • test_detail_schema_with_optionals() - Optional fields support
  • DetailSchemaModelSerializerTestCase in tests/views/test_viewset.py:

    • test_read_schema_has_minimal_fields() - ReadSerializer has minimal fields
    • test_detail_schema_has_extended_fields() - DetailSerializer has extended fields
    • test_get_retrieve_schema_returns_detail() - Retrieve uses detail schema
    • test_get_schemas_returns_four_tuple() - get_schemas returns 4-tuple
  • DetailSchemaSerializerTestCase - Tests for Serializer class with schema_detail

  • DetailSchemaFallbackTestCase - Tests fallback to schema_out when no detail defined

  • New M2M serializer_class Test Cases:

  • M2MRelationSchemaSerializerClassTestCase - Tests M2M with serializer_class
  • M2MRelationSchemaValidationTestCase:

    • test_serializer_class_with_plain_model_succeeds()
    • test_model_serializer_auto_generates_related_schema()
    • test_serializer_class_with_model_serializer_raises_error()
    • test_plain_model_without_serializer_class_or_related_schema_raises_error()
    • test_explicit_related_schema_takes_precedence()
  • New Test Model:

  • TestModelSerializerWithDetail in tests/test_app/models.py
  • Demonstrates separate ReadSerializer and DetailSerializer configurations

  • Updated Existing Tests:

  • All schemas property definitions updated to return 4-tuple format
  • test_get_schemas updated to expect 4 elements instead of 3
  • Refactored ManyToManyAPITestCase into Tests.BaseManyToManyAPITestCase base class

🔧 Internal Changes

  • Schema Mapping Updates:
  • _SCHEMA_META_MAP now includes "detail": "DetailSerializer" for ModelSerializer
  • _SERIALIZER_CONFIG_MAP now includes "detail": "detail" for Serializer
  • _get_serializer_config() updated to handle "detail" case

  • ModelSerializer Changes:

  • New DetailSerializer inner class with fields, customs, optionals, excludes attributes
  • _generate_model_schema() updated to handle "Detail" schema type
  • Schema naming: "Out"{model}SchemaOut, "Detail"{model}DetailSchemaOut

  • Serializer.Meta Changes:

  • New schema_detail: Optional[SchemaModelConfig] attribute
  • model_dump() now uses detail schema when available for single object serialization

  • M2MRelationSchema Changes:

  • New serializer_class: Optional[SerializerMeta] field
  • validate_related_schema() validator updated to handle serializer_class
  • ManyToManyAPI updated to pass serializer_class to ModelUtil

🚀 Use Cases & Examples

Detail Schema for Performance Optimization

```python from ninja_aio.models import ModelSerializer from django.db import models

class Article(ModelSerializer): title = models.CharField(max_length=200) summary = models.TextField() content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag) view_count = models.IntegerField(default=0)

class ReadSerializer:
    # List view: minimal fields for performance
    fields = ["id", "title", "summary", "author"]

class DetailSerializer:
    # Detail view: all fields including expensive relations
    fields = ["id", "title", "summary", "content", "author", "tags", "view_count"]
    customs = [
        ("reading_time", int, lambda obj: len(obj.content.split()) // 200),
    ]

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): pass # Schemas auto-generated from model ```

Endpoint Behavior: - GET /articles/ returns [{"id": 1, "title": "...", "summary": "...", "author": {...}}, ...] - GET /articles/1 returns {"id": 1, "title": "...", "summary": "...", "content": "...", "author": {...}, "tags": [...], "view_count": 1234, "reading_time": 5}

Detail Schema with Serializer Class

```python from ninja_aio.models import serializers

class ArticleSerializer(serializers.Serializer): class Meta: model = models.Article schema_out = serializers.SchemaModelConfig( # List view: minimal fields fields=["id", "title", "summary"] ) schema_detail = serializers.SchemaModelConfig( # Detail view: all fields fields=["id", "title", "summary", "content", "author", "tags"], customs=[("reading_time", int, lambda obj: len(obj.content.split()) // 200)] )

@api.viewset(model=models.Article) class ArticleViewSet(APIViewSet): serializer_class = ArticleSerializer ```

M2M with serializer_class

```python from ninja_aio.models import serializers from ninja_aio.schemas import M2MRelationSchema

class TagSerializer(serializers.Serializer): class Meta: model = Tag schema_out = serializers.SchemaModelConfig(fields=["id", "name"])

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): m2m_relations = [ M2MRelationSchema( model=Tag, # plain Django model related_name="tags", serializer_class=TagSerializer, # auto-generates related_schema add=True, remove=True, get=True, ) ] ```


🔍 Migration Guide

Using Detail Schemas

No migration required! Detail schema support is fully backward compatible:

```python

Existing code continues to work (no DetailSerializer = uses schema_out for retrieve)

class Article(ModelSerializer): class ReadSerializer: fields = ["id", "title", "content"] # Used for both list AND retrieve

New: Add DetailSerializer for different retrieve response

class Article(ModelSerializer): class ReadSerializer: fields = ["id", "title"] # Used for list only

class DetailSerializer:
    fields = ["id", "title", "content", "author", "tags"]  # Used for retrieve

```

Using serializer_class in M2MRelationSchema

```python

Before (v2.7.0) - Must provide related_schema manually

M2MRelationSchema( model=Tag, related_name="tags", related_schema=TagOut, # Must define this schema manually )

After (v2.8.0) - Can use serializer_class instead

M2MRelationSchema( model=Tag, related_name="tags", serializer_class=TagSerializer, # Auto-generates related_schema! ) ```

Updating Custom ViewSet Subclasses

If you override get_schemas(), update to return 4-tuple:

```python

Before (v2.7.0)

def get_schemas(self): return (schema_out, schema_in, schema_update)

After (v2.8.0)

def get_schemas(self): return (schema_out, schema_detail, schema_in, schema_update) ```


🎯 When to Use Detail Schema

  • Performance Optimization: Return minimal fields in list views, full details in retrieve
  • API Design: Clients get summaries in lists, full objects on individual requests
  • Expensive Relations: Avoid loading M2M/reverse relations for list endpoints
  • Computed Fields: Only compute expensive fields for single object retrieval
  • Bandwidth Optimization: Reduce payload size for list responses

📊 Performance Benefits

| Scenario | Without Detail Schema | With Detail Schema | |----------|----------------------|-------------------| | List 100 articles | Returns 100 × full content | Returns 100 × summary only | | Load M2M tags | Loaded for all 100 items | Only loaded for single retrieve | | Computed fields | Calculated for all items | Only calculated on retrieve | | Response size | Large (full content) | Optimized per endpoint |


⚠️ Important Notes

  • Fallback Behavior: If DetailSerializer/schema_detail not defined, retrieve uses schema_out
  • Schema Generation: generate_detail_s() returns None if no detail config exists
  • Backward Compatibility: All existing code works without changes
  • 4-Tuple Return: get_schemas() now returns 4 values instead of 3
  • M2M Validation: Cannot use serializer_class with ModelSerializer models

🙏 Acknowledgments

This release focuses on: - Enhanced API design flexibility with separate list/detail schemas - Performance optimization for list endpoints - Better M2M relation configuration options - Improved code organization and maintainability


🔗 Links


📦 Quick Start with Detail Schema

```python from ninja_aio.models import ModelSerializer from ninja_aio.views import APIViewSet from ninja_aio import NinjaAIO from django.db import models

api = NinjaAIO(title="My API")

Step 1: Define your model with ReadSerializer and DetailSerializer

class Article(ModelSerializer): title = models.CharField(max_length=200) summary = models.TextField() content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) tags = models.ManyToManyField(Tag)

class ReadSerializer:
    fields = ["id", "title", "summary"]  # Minimal for list

class DetailSerializer:
    fields = ["id", "title", "summary", "content", "author", "tags"]  # Full for retrieve

Step 2: Create your ViewSet (schemas auto-generated!)

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): pass

That's it! Your API now has optimized list and detail endpoints:

GET /articles/ → Returns list with minimal fields

GET /articles/{pk} → Returns single article with all fields

```


Version History

For older versions, please refer to the GitHub releases page.

v2.7.0
diff
2026-01-13notes
Release notes

Release Notes

[v2.7.0] - 2026-01-13


✨ Added

  • Union Type Support for Polymorphic Relations:
  • relations_serializers now accepts Union[SerializerA, SerializerB] to handle polymorphic relationships
  • Enables flexible handling of generic foreign keys, content types, and multi-model relations
  • Direct class references: Union[SerializerA, SerializerB]
  • String references: Union["SerializerA", "SerializerB"]
  • Mixed references: Union[SerializerA, "SerializerB"]
  • Absolute import paths: Union["myapp.serializers.SerializerA", SerializerB]
  • Lazy resolution of union members supports forward/circular dependencies
  • Schema generator creates union of all possible schemas automatically

  • Absolute Import Path Support for String References:

  • String references now support absolute import paths using dot notation
  • Example: "myapp.serializers.UserSerializer" or "users.serializers.UserSerializer"
  • Enables cross-module serializer references without circular import issues
  • Automatic module importing when needed (uses importlib.import_module())
  • Resolves lazily when schemas are generated
  • Works seamlessly with Union types

🛠 Changed

  • Enhanced Serializer Reference Resolution:
  • Now handles Union types by recursively resolving each member
  • Handles ForwardRef objects created by string type hints in unions (e.g., Union["StringType"])
  • Optimizes single-type unions by returning the single type directly

  • Enhanced Relation Schema Generation:

  • Generates union schemas when serializer reference is a Union type
  • Maintains full backward compatibility with single serializer references
  • Automatically filters out None schemas from union members

  • Updated Type Hints:

  • All serializer methods updated to reflect Union support
  • Better type safety for Union[Schema, ...] return values
  • Clearer documentation of acceptable input types

📝 Documentation

  • Comprehensive Union Types Documentation in docs/api/models/serializers.md:
  • New "Union Types for Polymorphic Relations" section:

    • Complete explanation of Union support with real-world examples
    • Basic polymorphic example with Video and Image serializers
    • All four Union type format variations documented with code samples
    • Use cases: polymorphic relations, flexible APIs, gradual migrations, multi-tenant systems
    • Complete polymorphic example using Django's GenericForeignKey
    • BlogPost/Product/Event example showing complex multi-model relations
  • New "String Reference Formats" section:

    • Local class name format: "ArticleSerializer"
    • Absolute import path format: "myapp.serializers.ArticleSerializer"
    • Requirements and resolution behavior documented
    • Cross-module references example with circular dependencies
  • Enhanced Configuration Section:

    • relations_serializers parameter updated to document Union support
    • Clear explanation: "Serializer class, string reference, or Union of serializers"
    • Forward/circular dependencies and polymorphic relations highlighted
    • Updated comparison table showing Union support feature
  • Updated Key Features:

    • Added Union types for polymorphic relations to key features list
    • Updated notes to mention Union type lazy resolution
    • Added note about schema generator creating unions
  • Code Examples and Best Practices:

  • Video/Image comment example for basic polymorphic relations
  • BlogPost/Product/Event example for complex GenericForeignKey usage
  • Cross-module circular reference example (Article ↔ User)
  • All four Union format variations with syntax examples

🧪 Tests

  • New Comprehensive Test Suite - UnionSerializerTestCase in tests/test_serializers.py

  • Module-Level Test Serializers:

  • AltSerializer - Alternative serializer with different field set (id, name)
  • AltStringSerializer - String reference test serializer (id, description)
  • MixedAltSerializer - Mixed reference test serializer (id, name, description)
  • LocalTestSerializer - Local reference test serializer (id only)

🔧 Internal Changes

  • Python 3.10+ Compatibility Fix:
  • Union types created using Union[tuple] syntax for compatibility
  • Replaced incompatible reduce(or_, resolved_types) pattern
  • Works correctly across Python 3.10, 3.11, 3.12+
  • No dependency on functools.reduce or operator.or_
  • Uses Python's typing system to expand Union[tuple] automatically

  • Code Organization:

  • Extracted string resolution logic into dedicated _resolve_string_reference() method
  • Extracted union schema generation into dedicated _generate_union_schema() method
  • Improved separation of concerns and code reusability
  • Better error messages with full import paths in exceptions

🚀 Use Cases & Examples

Basic Polymorphic Relations

```python from typing import Union from ninja_aio.models import serializers

class VideoSerializer(serializers.Serializer): class Meta: model = models.Video schema_out = serializers.SchemaModelConfig( fields=["id", "title", "duration", "url"] )

class ImageSerializer(serializers.Serializer): class Meta: model = models.Image schema_out = serializers.SchemaModelConfig( fields=["id", "title", "width", "height", "url"] )

class CommentSerializer(serializers.Serializer): class Meta: model = models.Comment schema_out = serializers.SchemaModelConfig( fields=["id", "text", "content_object"] ) relations_serializers = { "content_object": Union[VideoSerializer, ImageSerializer], } ```

Cross-Module References

```python

myapp/serializers.py

class ArticleSerializer(serializers.Serializer): class Meta: model = models.Article schema_out = serializers.SchemaModelConfig( fields=["id", "title", "author"] ) relations_serializers = { "author": "users.serializers.UserSerializer", # Absolute path }

users/serializers.py

class UserSerializer(serializers.Serializer): class Meta: model = models.User schema_out = serializers.SchemaModelConfig( fields=["id", "username", "articles"] ) relations_serializers = { "articles": "myapp.serializers.ArticleSerializer", # Circular ref! } ```

Generic Foreign Keys

```python from django.contrib.contenttypes.fields import GenericForeignKey from typing import Union

class CommentSerializer(serializers.Serializer): class Meta: model = Comment schema_out = serializers.SchemaModelConfig( fields=["id", "text", "created_at", "content_object"] ) relations_serializers = { "content_object": Union[ BlogPostSerializer, ProductSerializer, EventSerializer ], } ```


🔍 Migration Guide

Using Union Types

No migration needed! Union support is fully backward compatible:

```python

Existing code continues to work

class MySerializer(serializers.Serializer): class Meta: model = MyModel relations_serializers = { "author": AuthorSerializer, # ✅ Still works }

New Union syntax available

class MySerializer(serializers.Serializer): class Meta: model = MyModel relations_serializers = { "content": Union[VideoSerializer, ImageSerializer], # ✅ New! } ```

Using Absolute Import Paths

Update string references to use absolute paths for cross-module references:

```python

Before (v2.6.1) - Only local references worked

relations_serializers = { "author": "AuthorSerializer", # Must be in same module }

After (v2.7.0) - Absolute paths supported

relations_serializers = { "author": "users.serializers.AuthorSerializer", # ✅ Cross-module! } ```

String Reference Formats

Both formats are supported:

```python relations_serializers = { # Local reference (same module) "field1": "LocalSerializer",

# Absolute import path (any module)
"field2": "myapp.serializers.RemoteSerializer",

# Union with mixed formats
"field3": Union["LocalSerializer", "myapp.other.RemoteSerializer"],

} ```


🎯 When to Use Union Types

  • Polymorphic Relations: Generic foreign keys, Django ContentType relations
  • Flexible APIs: Different response formats based on runtime type
  • Gradual Migrations: Transitioning between serializer implementations
  • Multi-Tenant Systems: Different serialization per tenant
  • Dynamic Content: CMS systems with multiple content types
  • Activity Feeds: Mixed content types in single endpoint

📊 Performance Notes

  • Lazy Resolution: Union members resolved only when schemas generated (no startup overhead)
  • Schema Caching: Generated schemas can be cached for better performance
  • Memory Efficient: Only generates schemas for types actually used
  • Import Optimization: Absolute paths only import modules when needed

⚠️ Important Notes

  • String References: Resolve within same module by default; use absolute paths for cross-module
  • Union Schema Generation: Creates union of all possible schemas from union members
  • Backward Compatibility: All existing code continues to work without changes
  • Python Version: Requires Python 3.10+ (Union syntax compatibility)
  • Type Validation: Union types provide type hints but runtime validation depends on your model logic

🙏 Acknowledgments

This release focuses on: - Enhanced flexibility for polymorphic relationships - Better support for complex project architectures - Improved developer experience with cross-module references - Python 3.10+ compatibility and modern typing features


🔗 Links


📦 Quick Start with Union Types

```python from typing import Union from ninja_aio.models import serializers

Step 1: Define your serializers

class VideoSerializer(serializers.Serializer): class Meta: model = Video schema_out = serializers.SchemaModelConfig(fields=["id", "title", "url"])

class ImageSerializer(serializers.Serializer): class Meta: model = Image schema_out = serializers.SchemaModelConfig(fields=["id", "title", "url"])

Step 2: Use Union in relations_serializers

class CommentSerializer(serializers.Serializer): class Meta: model = Comment schema_out = serializers.SchemaModelConfig( fields=["id", "text", "content_object"] ) relations_serializers = { "content_object": Union[VideoSerializer, ImageSerializer], }

Step 3: Use with APIViewSet (automatic!)

@api.viewset(model=Comment) class CommentViewSet(APIViewSet): serializer_class = CommentSerializer # Union types work automatically! ```


Version History

For older versions, please refer to the GitHub releases page.

v2.6.1
diff
2026-01-12notes
Release notes

Release Notes

[v2.6.1] - 2026-01-12


✨ Added

  • String Reference Support for Relations:
  • relations_serializers now accepts string references (e.g., "ArticleSerializer") in addition to class references
  • Enables forward references and circular dependencies between serializers
  • Lazy resolution of serializer references when schemas are generated

  • New Internal Methods:

  • BaseSerializer._resolve_serializer_reference() - Resolves string or class serializer references
  • BaseSerializer._resolve_relation_schema() - Centralized relation schema resolution logic

🛠 Changed

  • Schema Generation Lifecycle:
  • Removed eager schema generation from Serializer.__init_subclass__()
  • Schemas are now generated on-demand via explicit calls to generate_*() methods
  • Removed cached schema properties (.schema_in, .schema_out, .schema_update, .schema_related)
  • Breaking: Must use generate_create_s(), generate_read_s(), etc. instead of accessing properties

  • Internal Refactoring:

  • Replaced match/case with if/elif statements in _generate_model_schema() for better readability
  • Added configuration mapping dictionaries (_SERIALIZER_CONFIG_MAP) to simplify lookups
  • Consolidated duplicate schema resolution logic in relation handling methods
  • Improved code organization with clearer comments and structure

  • APIViewSet Integration:

  • Added serializer instance property initialized from serializer_class()
  • Better integration with on-demand schema generation

📝 Documentation

  • Updated Serializer Documentation:
  • Added "String References for Forward/Circular Dependencies" section with examples
  • Updated "Schema Generation" section to clarify on-demand generation
  • Removed outdated references to eager schema generation
  • Updated comparison table: "Auto-binding" → "Schema generation"
  • Enhanced configuration section with bold formatting and clearer descriptions

  • Key Documentation Changes:

  • Emphasized that generate_*() methods must be called explicitly
  • Documented string reference requirements (same module, lazy resolution)
  • Added circular dependency example with AuthorSerializerArticleSerializer

⚠ Breaking Changes & Migration Notes

Removed Schema Properties

Schema properties have been removed from Serializer class. You must now explicitly call generation methods:

```python

Before (v2.5.0) - NO LONGER WORKS

ArticleSerializer.schema_in # ❌ AttributeError ArticleSerializer.schema_out # ❌ AttributeError ArticleSerializer.schema_update # ❌ AttributeError ArticleSerializer.schema_related # ❌ AttributeError

After (v2.6.0) - Explicit generation required

ArticleSerializer.generate_create_s() # ✅ Returns create schema ArticleSerializer.generate_read_s() # ✅ Returns read schema ArticleSerializer.generate_update_s() # ✅ Returns update schema ArticleSerializer.generate_related_s() # ✅ Returns related schema ```

Note: This change typically doesn't affect user code since these methods are called internally by APIViewSet. Only relevant if you're calling these methods directly.


🔍 Migration Guide

1. Update Schema Access in Custom Code

If you're directly accessing schema properties, update to use generation methods:

```python

Before (v2.5.0)

class ArticleViewSet(APIViewSet): def get_schemas(self): return { "in": self.serializer_class.schema_in, # ❌ No longer works "out": self.serializer_class.schema_out, # ❌ No longer works }

After (v2.6.0)

class ArticleViewSet(APIViewSet): def get_schemas(self): return { "in": self.serializer_class.generate_create_s(), # ✅ Explicit generation "out": self.serializer_class.generate_read_s(), # ✅ Explicit generation } ```

2. Use String References for Circular Dependencies

Take advantage of string references to simplify circular dependencies:

```python

Before (v2.5.0) - Workarounds needed for circular refs

class AuthorSerializer(serializers.Serializer): class Meta: model = models.Author schema_out = serializers.SchemaModelConfig( fields=["id", "name", "articles"] ) # Had to carefully order class definitions or use late binding

After (v2.6.0) - String references make it easy

class AuthorSerializer(serializers.Serializer): class Meta: model = models.Author schema_out = serializers.SchemaModelConfig( fields=["id", "name", "articles"] ) relations_serializers = { "articles": "ArticleSerializer", # ✅ Forward reference }

class ArticleSerializer(serializers.Serializer): class Meta: model = models.Article schema_out = serializers.SchemaModelConfig( fields=["id", "title", "author"] ) relations_serializers = { "author": "AuthorSerializer", # ✅ Circular reference works! } ```

String Reference Requirements: - Must be the exact class name as a string - Serializer must be defined in the same module - Resolution happens lazily when generate_*() is called - Both forward and circular references are supported

3. Schema Generation Best Practices

In APIViewSet (no changes needed): ```python

APIViewSet handles schema generation automatically

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): serializer_class = ArticleSerializer # No changes needed - works automatically ```

In Custom Code (call generate methods): ```python

Explicit schema generation when needed

from ninja import Router

router = Router()

@router.post("/articles/", response=ArticleSerializer.generate_read_s()) async def create_article(request, payload: ArticleSerializer.generate_create_s()): serializer = ArticleSerializer() instance = await serializer.create(payload.model_dump()) return await serializer.model_dump(instance) ```

Caching Schemas (if needed for performance): ```python

Cache schemas at module level if generating repeatedly

ARTICLE_CREATE_SCHEMA = ArticleSerializer.generate_create_s() ARTICLE_READ_SCHEMA = ArticleSerializer.generate_read_s()

@router.post("/articles/", response=ARTICLE_READ_SCHEMA) async def create_article(payload: ARTICLE_CREATE_SCHEMA): # Use cached schemas pass ```

4. Complete Migration Example

Here's a complete before/after example:

```python

Before (v2.5.0)

from ninja_aio.models import serializers from ninja_aio import NinjaAIO from ninja_aio.views import APIViewSet

class AuthorSerializer(serializers.Serializer): class Meta: model = models.Author schema_out = serializers.SchemaModelConfig( fields=["id", "name"] )

class ArticleSerializer(serializers.Serializer): class Meta: model = models.Article schema_out = serializers.SchemaModelConfig( fields=["id", "title", "author"] ) relations_serializers = { "author": AuthorSerializer, # Required class ordering }

Access schemas (no longer works)

create_schema = ArticleSerializer.schema_in # ❌ read_schema = ArticleSerializer.schema_out # ❌

After (v2.6.0)

from ninja_aio.models import serializers from ninja_aio import NinjaAIO from ninja_aio.views import APIViewSet

class AuthorSerializer(serializers.Serializer): class Meta: model = models.Author schema_out = serializers.SchemaModelConfig( fields=["id", "name", "articles"] ) relations_serializers = { "articles": "ArticleSerializer", # ✅ String reference }

class ArticleSerializer(serializers.Serializer): class Meta: model = models.Article schema_out = serializers.SchemaModelConfig( fields=["id", "title", "author"] ) relations_serializers = { "author": "AuthorSerializer", # ✅ Circular reference! }

Explicit schema generation

create_schema = ArticleSerializer.generate_create_s() # ✅ read_schema = ArticleSerializer.generate_read_s() # ✅

Using with APIViewSet (no changes needed)

api = NinjaAIO()

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): serializer_class = ArticleSerializer # Automatically works with on-demand generation ```


🐛 Bug Fixes

  • Fixed lazy resolution issues with forward and circular serializer references
  • Improved error messages when string references cannot be resolved
  • Corrected model_dump() and models_dump() to use explicit schema generation
  • Fixed potential issues with model_util vs util attribute naming

🚀 Performance Improvements

  • Reduced Initialization Overhead: Schemas only generated when actually needed
  • Memory Efficiency: Unused schemas are never created
  • Lazy Resolution: String references resolved on-demand, reducing startup time
  • Faster Imports: Removed eager schema generation from module import time

📊 Code Quality Improvements

  • Reduced Code Duplication:
  • Extracted common relation resolution logic into _resolve_relation_schema()
  • Consolidated duplicate code in _build_schema_reverse_rel() and _build_schema_forward_rel()
  • Reduced relation handling code by ~40 lines

  • Improved Maintainability:

  • Replaced match/case with clearer if/elif statements
  • Added configuration mapping dictionaries for cleaner lookups
  • Better code organization with descriptive comments
  • Consistent use of any() for empty checks

  • Better Readability:

  • Flattened nesting in _generate_model_schema()
  • Clearer separation between special cases and standard logic
  • Improved docstrings and parameter descriptions
  • More descriptive variable names

🙏 Acknowledgments

This release focuses on: - Architectural improvements for forward/circular dependency support - Cleaner, more maintainable internal code structure - On-demand resource generation for better performance - Enhanced developer experience with string references


📝 Notes

  • Schema Generation: While properties were removed, APIViewSet automatically calls generate_*() methods, so most applications won't need code changes

  • Performance: On-demand generation typically improves startup time. If you need schemas multiple times, consider caching them at module level

  • String References: Only resolve within the same module. For cross-module references, use direct class imports

  • Backward Compatibility: Code using APIViewSet continues to work without changes. Direct schema property access will raise AttributeError

  • Internal Refactoring: This release includes significant internal refactoring for code quality without changing public APIs (except removal of schema properties)


🔗 Links


📦 Upgrade Checklist

Use this checklist when upgrading from v2.5.0 to v2.6.0:

  • [ ] Search codebase for .schema_in, .schema_out, .schema_update, .schema_related property access
  • [ ] Replace with generate_create_s(), generate_read_s(), generate_update_s(), generate_related_s() calls
  • [ ] Update any circular serializer references to use string references
  • [ ] Review custom create() and update() method implementations (if any)
  • [ ] Test all CRUD endpoints to ensure proper functionality
  • [ ] Update any schema caching logic to use explicit generation
  • [ ] Review and update API documentation if it references old property access

v2.5.0
diff
2026-01-12notes
Release notes

Release Notes

[v2.5.0] - 2026-01-12


✨ Added

  • APIViewSet Enhancements:
  • model_verbose_name and model_verbose_name_plural attributes for display name customization
  • Automatic transaction wrapping on create, update, and delete operations

  • ModelUtil Query Methods:

  • New properties:
    • with_serializer - Check if serializer_class is attached
    • pk_field_type - Python type corresponding to the primary key field
    • model_name - Django internal model name

🛠 Changed

  • APIViewSet:
  • CRUD views now automatically decorated with @aatomic for transactional integrity
  • Enhanced get_schemas() method for unified schema generation from both ModelSerializer and Serializer

  • ModelUtil:

  • Query optimization merging logic improved to respect both model and serializer configurations

  • Serializer Lifecycle Hooks:

  • All Serializer hooks now consistently receive instance parameter
  • Inline execution of before/after save hooks integrated with @aatomic decorator
  • Hook signatures standardized: custom_actions(payload, instance), post_create(instance), before_save(instance), etc.

📝 Documentation

  • New Documentation Pages:
  • Transaction Management section in APIViewSet docs
  • Extra Decorators section with examples and configuration
  • Enhanced ModelUtil properties documentation
  • Query method parameter documentation with detailed examples

  • Enhanced Content:

  • APIViewSet Core Attributes table updated with new fields
  • Serializer lifecycle hooks section with complete signature examples
  • ModelUtil method signatures with all parameters documented
  • CRUD operation flows documented for Serializer pattern

⚠ Breaking Changes & Migration Notes

Transaction Behavior (New Default)

Create, update, and delete operations are now automatically wrapped in database transactions:

```python

Automatic transaction wrapping (new in v2.5.0)

@api.viewset(model=Article) class ArticleViewSet(APIViewSet): pass # create/update/delete wrapped in @aatomic ```

Migration: If you were manually managing transactions in lifecycle hooks, you may encounter nested transaction issues. Remove manual transaction management:

```python

Before (v2.4.0)

async def post_create(self, instance): async with transaction.atomic(): # Remove this await AuditLog.objects.acreate(...)

After (v2.5.0)

async def post_create(self, instance): # Transaction already managed by @aatomic await AuditLog.objects.acreate(...) ```

Serializer Hook Signatures

Added Serializer hooks signatures, they are standardized to always receive instance:

```python

v2.5.0 - Standardized (always receive instance)

class MySerializer(Serializer): async def custom_actions(self, payload, instance): # instance parameter required pass

async def post_create(self, instance):
    # instance parameter required
    pass

def before_save(self, instance):
    # instance parameter required
    pass

def after_save(self, instance):
    # instance parameter required
    pass

def on_delete(self, instance):
    # instance parameter required
    pass

```


🔍 Migration Guide

1. Updating Serializer Lifecycle Hooks

If you're using Serializer (Meta-driven pattern), update hook signatures to receive instance parameter:

```python from ninja_aio.models import serializers from asgiref.sync import sync_to_async

class ArticleSerializer(serializers.Serializer): class Meta: model = Article schema_in = serializers.SchemaModelConfig( fields=["title", "content", "author"], customs=[("send_notification", bool, True)] ) schema_out = serializers.SchemaModelConfig( fields=["id", "title", "content", "author", "created_at"] )

# Async hooks - receive instance parameter
async def custom_actions(self, payload, instance):
    """Execute custom logic after field assignment."""
    if payload.get("send_notification"):
        # Access instance fields
        await send_email(
            instance.author.email,
            f"Article created: {instance.title}"
        )

async def post_create(self, instance):
    """Hook after first save (creation only)."""
    await AuditLog.objects.acreate(
        action="article_created",
        article_id=instance.id,
        user_id=instance.author_id
    )

# Sync hooks - also receive instance parameter
def before_save(self, instance):
    """Modify instance before save."""
    from django.utils.text import slugify
    if not instance.slug:
        instance.slug = slugify(instance.title)

def after_save(self, instance):
    """Execute logic after save."""
    # Clear cache
    from django.core.cache import cache
    cache.delete(f"article:{instance.id}")

def on_create_before_save(self, instance):
    """Before save, creation only."""
    instance.view_count = 0

def on_create_after_save(self, instance):
    """After save, creation only."""
    # Log creation
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"Article {instance.id} created")

def on_delete(self, instance):
    """After deletion."""
    import logging
    logger = logging.getLogger(__name__)
    logger.info(f"Article {instance.id} deleted")

```

Key Points: - All hooks receive instance as a parameter - Async hooks: custom_actions(payload, instance), post_create(instance) - Sync hooks: before_save(instance), after_save(instance), on_delete(instance) - Creation-specific hooks: on_create_before_save(instance), on_create_after_save(instance)

2. Configuring QuerySet Optimization

Add QuerySet configuration to your Serializer or ModelSerializer for automatic query optimization:

```python from ninja_aio.models import serializers from ninja_aio.schemas.helpers import ModelQuerySetSchema, ModelQuerySetExtraSchema

class ArticleSerializer(serializers.Serializer): class Meta: model = Article schema_out = serializers.SchemaModelConfig( fields=["id", "title", "content", "author", "category", "tags"] ) relations_serializers = { "author": AuthorSerializer, "category": CategorySerializer, "tags": TagSerializer, }

class QuerySet:
    # Applied to list and retrieve operations
    read = ModelQuerySetSchema(
        select_related=["author", "category"],
        prefetch_related=["tags"],
    )

    # Applied when queryset_request hook is called
    queryset_request = ModelQuerySetSchema(
        select_related=["author__profile"],
        prefetch_related=["comments", "comments__author"],
    )

    # Named scopes for specific use cases
    extras = [
        ModelQuerySetExtraSchema(
            scope="detail_view",
            select_related=["author", "author__profile", "category"],
            prefetch_related=["tags", "comments", "comments__author"],
        ),
        ModelQuerySetExtraSchema(
            scope="list_view",
            select_related=["author", "category"],
            prefetch_related=["tags"],
        ),
    ]

@classmethod
async def queryset_request(cls, request):
    """
    Optional: Customize queryset based on request.
    Automatically enhanced with QuerySet.queryset_request optimizations.
    """
    qs = cls._meta.model.objects.all()

    # Filter based on user permissions
    if not request.user.is_staff:
        qs = qs.filter(is_published=True)

    # Add request-specific filters
    if request.GET.get("featured"):
        qs = qs.filter(is_featured=True)

    return qs

```

For ModelSerializer:

```python from ninja_aio.models import ModelSerializer from ninja_aio.schemas.helpers import ModelQuerySetSchema from django.db import models

class Article(ModelSerializer): title = models.CharField(max_length=200) content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True) tags = models.ManyToManyField(Tag, related_name="articles")

class ReadSerializer:
    fields = ["id", "title", "content", "author", "category", "tags"]

class QuerySet:
    read = ModelQuerySetSchema(
        select_related=["author", "category"],
        prefetch_related=["tags"],
    )
    queryset_request = ModelQuerySetSchema(
        select_related=["author__profile"],
        prefetch_related=["comments"],
    )

@classmethod
async def queryset_request(cls, request):
    """Optimize queries for this model."""
    return cls.objects.select_related("author", "category")

```

How QuerySet Configuration Works:

  1. read: Applied automatically to list and retrieve operations when is_for_read=True
  2. queryset_request: Applied when with_qs_request=True (default) in get_objects() or get_object()
  3. extras: Named scopes accessible via QueryUtil.SCOPES for custom scenarios
  4. Merging: Optimizations from multiple sources are merged (no duplicates)

Benefits: - Eliminates N+1 queries automatically - Centralizes query optimization configuration - Works with both ModelSerializer and Serializer patterns - Optimizations apply to all CRUD operations

3. Customizing Model Display Names

Override verbose names without modifying models:

python @api.viewset(model=Article) class ArticleViewSet(APIViewSet): model_verbose_name = "Blog Post" model_verbose_name_plural = "Blog Posts" # OpenAPI will use "Blog Post" instead of "Article"


🐛 Bug Fixes

  • Fixed query optimization merging when both model and serializer provide hints
  • Corrected read_s() behavior when both instance and query_data provided (now raises clear error)
  • Improved error messages for missing primary key in get_object()
  • Fixed duplicate route registration with @unique_view decorator

🚀 Performance Improvements

  • Transaction management with @aatomic reduces database round-trips
  • Query optimization merging eliminates redundant select_related/prefetch_related
  • with_qs_request parameter allows skipping hook when not needed

🙏 Acknowledgments

This release focuses on: - Enhanced transaction safety - Flexible query control - Per-operation customization - Comprehensive documentation


📝 Notes

  • Backward Compatibility: All v2.4.0 code continues to work. New parameters have sensible defaults.

  • Transaction Overhead: The @aatomic decorator adds minimal overhead. If you need non-transactional operations, override the view methods directly.

  • Query Parameters: with_qs_request defaults to True to maintain v2.4.0 behavior. Set to False to skip the queryset_request hook.

  • Serializer Hooks: If migrating from v2.4.0 Serializer usage, ensure all hooks accept the instance parameter.


🔗 Links


v2.4.0
diff
2026-01-09notes
Release notes

Release Notes

[v2.4.0] - 2026-01-09


✨ Added

  • Serializer (Meta-driven):
  • New Serializer for vanilla Django models configured via nested Meta (no ModelSerializer inheritance).
  • Dynamic schema generation helpers: generate_read_s, generate_create_s, generate_update_s, generate_related_s.
  • Relation handling via relations_serializers for forward and reverse relations.
  • APIViewSet:
  • serializer_class to auto-generate missing schemas for non-ModelSerializer models and drive queryset_request.
  • ModelUtil:
  • Accepts serializer_class to build querysets using Serializer.queryset_request when provided.
  • Docs/README:
  • New docs page: api/models/serializers.md with usage and examples.
  • README sections/examples for Meta-driven Serializer.
  • MkDocs nav entry for Serializer.
  • Tests:
  • Serializer tests (forward and reverse relations).
  • Viewset tests using serializer_class-backed endpoints.

🛠 Changed

  • Package layout:
  • ninja_aio/models/init.py now exports ModelUtil and ModelSerializer.
  • models.py refactored to models/utils.py; ModelSerializer moved to models/serializers.py.
  • API internals:
  • APIViewSet.compute_schema generates schemas from serializer_class for vanilla models; retains model-backed generation for ModelSerializer.
  • ModelUtil.get_queryset_request uses serializer_class when provided.
  • Docs:
  • Index formatting tweaks; added links and examples for Serializer docs.

📝 Documentation

  • Serializer (Meta-driven):
  • Configure via Meta: model, schema_in, schema_out, schema_update, relations_serializers.
  • Examples for FK and reverse relations; customs and optionals.
  • APIViewSet:
  • Using serializer_class to auto-generate schemas and plug into queryset_request.
  • README:
  • Quick example attaching Serializer to APIViewSet.
  • MkDocs:
  • Added nav entry under Models: Serializer (Meta-driven).

⚠ Notes / Potential Impact

  • Relation serializers:
  • Reverse relations on vanilla models need relations_serializers entries to include nested schemas; otherwise skipped unless the related model is a ModelSerializer.
  • A UserWarning is emitted when a reverse relation is listed without a mapping; suppressed in tests via NINJA_AIO_TESTING=True.
  • Refactor:
  • Imports may need updates due to ModelUtil relocation and new serializers module.

🔍 Migration / Action

  1. Define a Meta-driven Serializer for existing Django models and attach it to APIViewSet via serializer_class.
  2. Provide relations_serializers for reverse relations to include nested schemas on read.
  3. Update imports:
  4. from ninja_aio.models import ModelUtil, ModelSerializer
  5. from ninja_aio.models.serializers import Serializer, SchemaModelConfig
  6. If relying on queryset_request with vanilla models, implement Serializer.queryset_request; APIViewSet and ModelUtil will use it automatically.
v2.3.2
diff
2026-01-08notes
Release notes

[v2.3.2] - 2026-01-08


✨ Added

  • Support Url pydantic field serialization
  • Support for django ninja until 1.6

v2.3.1
diff
2026-01-07notes
Release notes

[v2.3.1] - 2026-01-07


✨ Added

  • CI/Docs Deployment:
  • GitHub workflow updated to recognize and manage version "2.3" for docs deploy/delete.
  • Documentation/README:
  • Added link to the external example repository: https://github.com/caspel26/ninja-aio-blog-example.

🛠 Changed

  • README/Docs:
  • Switched examples to decorator-first style with @api.viewset(Model) and in-class method decorators (e.g., @api_post).
  • Removed explicit api = api and model = ... from examples where @api.viewset(...) is used; emphasized automatic registration.
  • Cleaned and reformatted examples and quick links table; clarified usage in the index page and decorators page.
  • Packaging:
  • Python version spec adjusted from >=3.10, <=3.14 to >=3.10, <3.15 in pyproject metadata.

🗑 Removed

  • In-repo example apps:
  • Deleted examples/ex_1 and examples/ex_2 (models, views, urls, and auth). Examples are now hosted in the external repository linked in the README.

📝 Documentation

  • Index and README updated to prefer @api.viewset(Model) and decorator-based custom endpoints.
  • Decorators page (docs/api/views/decorators.md) revised to reflect decorator-first usage.
  • Added references to the external example repository for complete, runnable samples.

v2.3.0
diff
2026-01-04notes
Release notes

Release Notes

[v2.3.0] - 2026-01-04


✨ Added

  • Decorators:
  • New operation decorators for class methods: api_get, api_post, api_put, api_patch, api_delete, api_options, api_head (import from ninja_aio.decorators).
  • Utilities: decorate_view, aatomic, unique_view (now under ninja_aio.decorators).
  • Factory-backed decorators ensure clean OpenAPI signatures (exclude self) and support extra decorators like pagination.
  • Views:
  • APIView/APIViewSet auto-register decorated methods via lazy binding; no manual add_views_to_route() when using @api.view / @api.viewset.
  • APIViewSet supports global trailing slash setting via settings.NINJA_AIO_APPEND_SLASH (default True).
  • M2M:
  • M2MRelationSchema.append_slash to control trailing slash on the GET relation route.
  • Relation path normalization for consistent URLs whether path includes leading slash or not.
  • Tests/Examples:
  • Added decorator-based examples and tests for custom endpoints on views and viewsets.

🛠 Changed

  • README/Docs:
  • Prefer @api.viewset(Model) with decorator-based endpoints; legacy views() remains supported.
  • Clarified trailing slash behavior for CRUD retrieve paths and M2M relations.
  • Decorator-first examples across APIView and APIViewSet pages; cleaner OpenAPI notes.
  • API internals:
  • Base API class now binds decorator-registered methods via _add_views(); APIView/APIViewSet call super()._add_views() before legacy views().
  • APIViewSet path generation respects NINJA_AIO_APPEND_SLASH for retrieve path (/{pk}/ vs /{pk}).
  • Exceptions/Helpers:
  • Added docstrings for clearer behavior in exceptions, query helpers, and schemas.

📝 Documentation

  • APIView/APIViewSet:
  • Decorator-first usage with examples; automatic lazy registration; signature preservation.
  • Decorators:
  • Using operation decorators with extra decorators (e.g., paginate(PageNumberPagination), unique_view(name)).
  • ViewSet relations:
  • Per-relation append_slash; path normalization rules; trailing slash settings.
  • README:
  • Simplified setup: @api.viewset(Model) and decorator-based custom endpoints.

⚠ Notes / Potential Impact

  • Trailing slash:
  • Global NINJA_AIO_APPEND_SLASH defaults to True. Disable to remove trailing slash from retrieve paths.
  • M2M GET relation endpoints default to no trailing slash; enable per relation with append_slash=True.
  • Registration:
  • When using @api.view / @api.viewset, endpoints defined via decorators are mounted automatically; avoid redundant manual registration.
  • OpenAPI:
  • Decorator-backed handlers exclude self and preserve type hints for cleaner specs.

🔍 Migration / Action

  1. Adopt decorators for extra endpoints:
  2. APIView: annotate the class with @api.view(...), then decorate methods with @api_get("/path", ...).
  3. APIViewSet: annotate with @api.viewset(Model, ...), then use @api_get("/path", ...), @api_post(...), etc.
  4. Trailing slash configuration:
  5. Set NINJA_AIO_APPEND_SLASH=False in Django settings to drop trailing slash on retrieve paths globally.
  6. For M2M GET relations, use M2MRelationSchema(append_slash=True/False) to control trailing slash.
  7. Legacy support:
  8. views() continues to work; prefer decorators for clearer code and better OpenAPI.
  9. Docs/examples:
  10. Update references to new decorator modules and follow decorator-first examples.
v2.2.0
diff
2026-01-03notes
Release notes

Release Notes

[2.2.0] - 2026-01-03


✨ Added

  • API:
  • Decorators: NinjaAIO.view(prefix, tags) and NinjaAIO.viewset(model, prefix, tags) for automatic registration.
  • Base API class: shared attributes for APIView and APIViewSet (api, router_tags, api_route_path).
  • Views:
  • APIView: supports constructor args (api, prefix, tags) with router_tags and standardized error_codes.
  • APIViewSet: constructor (api, model, prefix, tags); infers base path from model when not provided; router_tags support.
  • Auth:
  • JwtKeys type expands to include jwk.OctKey (HMAC).
  • validate_key accepts jwk.OctKey.
  • encode_jwt/decode_jwt type hints generalized to JwtKeys.
  • Tests:
  • Added decorator-based tests for APIView and APIViewSet (ModelSerializer and plain Django model).
  • Updated ManyToMany tests to construct viewset with api argument.
  • Docs:
  • APIView and APIViewSet docs: “Recommended” decorator-based examples.
  • Mixins doc moved to docs/api/views/mixins.md.
  • Index updated with modern ModelSchema-based examples and async ORM usage.

🛠 Changed

  • Version:
  • Bumped to 2.2.0 in ninja_aio/__init__.py.
  • Error codes:
  • Standardized to {400, 401, 404}; removed 428 references in code and docs.
  • Docs:
  • docs/api/authentication.md: use list-based auth [JWTAuth(), APIKeyAuth()] instead of bitwise OR.
  • docs/api/views/api_view.md and api_view_set.md: emphasize decorator usage; cleaner examples; notes updated.
  • MkDocs nav: Mixins path updated to api/views/mixins.md.
  • API internals:
  • APIView/APIViewSet refactored to share base attributes, constructor supports api, prefix, and tags.
  • Docs workflow:
  • mike set-default --push latest when MAKE_LATEST is true.
  • Packaging:
  • Python requirement set to >=3.10, <=3.14 in pyproject.toml.

📝 Documentation

  • Updated:
  • Authentication: list-based auth configuration and clarified behavior.
  • APIView/APIViewSet: decorator-first usage, async compatibility, and standard error codes.
  • Index: ModelSchema In/Out patterns with async ORM examples.
  • Moved:
  • Mixins doc to api/views/mixins.md; MkDocs navigation adjusted.

⚠ Notes / Potential Impact

  • Error handling:
  • 428 code removed; rely on {400, 401, 404}.
  • Auth configuration:
  • Use lists for multiple auth methods; bitwise OR in docs deprecated.
  • Docs deployment:
  • Default alias set to “latest” on deploy when MAKE_LATEST=true.
  • Python compatibility:
  • Upper bound set to 3.14.

🔍 Migration / Action

  1. Adopt decorators:
  2. APIView: @api.view(prefix="/path", tags=[...])
  3. APIViewSet: @api.viewset(model=MyModel, prefix="/path", tags=[...])
  4. Update auth configuration:
  5. HMAC keys supported via jwk.OctKey where applicable.
  6. Error codes:
  7. Remove references/handlers for 428; standardize to {400, 401, 404}.
  8. Docs links:
  9. Update references to Mixins at api/views/mixins.md.
  10. Runtime:
  11. Ensure Python version is <= 3.14 per pyproject.toml.
v2.1.0
diff
2026-01-02notes
Release notes

Release Notes

[2.1.0] - 2026-01-01


✨ Added

  • Views:
  • ReadOnlyViewSet: list and retrieve-only endpoints.
  • WriteOnlyViewSet: create, update, and delete-only endpoints.
  • Exported via ninja_aio.views.__init__ for cleaner imports.
  • Mixins:
  • New filtering mixins under ninja_aio/views/mixins.py: IcontainsFilterViewSetMixin, BooleanFilterViewSetMixin, NumericFilterViewSetMixin, DateFilterViewSetMixin, and specialized Greater/Less variants.
  • Auth docs:
  • New docs/auth.md with JWT helpers and AsyncJwtBearer usage and configuration.
  • Tests:
  • Extended test model with age, active, and active_from fields.
  • Added viewset tests for mixins (icontains, boolean, numeric, date comparisons).
  • Added auth tests for JWT encode/decode and AsyncJwtBearer claim validation.
  • Docs navigation:
  • Added Mixins page and JWT & AsyncJwtBearer page to MkDocs nav.
  • MkDocs mike config sets default: latest.

🛠 Changed

  • Docs workflow (.github/workflows/docs.yml):
  • Safer deletion: requires explicit delete_version choice and delete_confirm, protects latest, stable, and current default.
  • make_latest default set to false.
  • Coverage workflow:
  • Bump codecov/codecov-action from v5.5.1 to v5.5.2.
  • API helpers:
  • Use decorate_view to compose unique_view and paginate for related GET endpoints.
  • APIViewSet (imports and behavior):
  • Module reorganized to ninja_aio/views/api.py with updated internal imports.
  • get_schemas: generates schemas only if missing when model is a ModelSerializerMeta, else returns explicitly set schemas.
  • Hook docs clarified to allow sync or async handlers for query params.
  • Auth:
  • encode_jwt: header now includes kid only when present (conditional merge).
  • Docs:
  • docs/api/views/api_view_set.md updated to document ReadOnlyViewSet and WriteOnlyViewSet.
  • docs/mixins.md aligned with implemented mixins and examples.

📝 Documentation

  • New:
  • JWT & AsyncJwtBearer guide with examples for settings and direct JWK usage.
  • Updated:
  • Mixins reference to match implemented classes and recommended query param types.
  • APIViewSet docs extended with ReadOnly/WriteOnly usage.

⚠ Notes / Potential Impact

  • Docs deployment:
  • Deletion requires explicit confirmation and cannot remove protected aliases or current default.
  • Mixins:
  • Date filters expect values that implement isoformat; prefer Pydantic date/datetime in query params.

🔍 Migration / Action

  1. Update imports:
  2. from ninja_aio.views import APIViewSet, ReadOnlyViewSet, WriteOnlyViewSet
  3. from ninja_aio.views import mixins for filter mixins.
  4. For related list endpoints using custom decorators, consider adopting decorate_view for consistent composition.
  5. If using JWT:
  6. Optionally set JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, JWT_ISSUER, JWT_AUDIENCE in Django settings.
  7. Validate claims via AsyncJwtBearer.claims registry and verify allowed algorithms.
  8. Review docs workflow inputs before deleting versions; use delete_confirm: true.
v2.0.0
diff
2025-12-16notes
Release notes

Release Notes

[2.0.0] - 2025-12-16


✨ Added

  • QueryUtil and query scopes:
  • New QueryUtil with SCOPES (READ, QUERYSET_REQUEST, plus extras) and apply_queryset_optimizations.
  • ModelSerializer.query_util bound per model via __init_subclass__.
  • ModelSerializer.QuerySet supports read, queryset_request, extras.
  • Query schemas:
  • QuerySchema, ObjectQuerySchema, ObjectsQuerySchema, ModelQuerySetSchema, ModelQuerySetExtraSchema, QueryUtilBaseScopesSchema.
  • ModelUtil:
  • get_objects(...): optimized queryset fetching with filters and select/prefetch hints.
  • get_object(...): single-object retrieval by pk or getters with optimizations.
  • read_s(...) and list_read_s(...): serialize instances or auto-fetch via query schemas.
  • Relation discovery helpers: get_select_relateds(), get_reverse_relations().
  • PK type resolution: pk_field_type with helpful error for unknown field types.
  • ManyToManyAPI:
  • GET related endpoints return {items: [...], count: N}.
  • Relation filter handlers accept sync or async functions.
  • Related items use ModelUtil.list_read_s for serialization.
  • Per-relation single-object resolution handler for POST: <related_name>_query_handler(...).
  • Schemas modularization:
  • New modules: ninja_aio/schemas/api.py, ninja_aio/schemas/generics.py, and exported names under ninja_aio/schemas/__init__.py.
  • Decorators:
  • decorate_view utility to compose multiple decorators (sync/async), skipping None.
  • APIViewSet.extra_decorators via DecoratorsSchema for per-operation decoration.
  • Renderer:
  • ORJSON renderer option via settings.NINJA_AIO_ORJSON_RENDERER_OPTION (bitmask, supports |).

🛠 Changed

  • APIViewSet:
  • List uses ModelUtil.get_objects and list_read_s with read optimizations; filter hooks retained.
  • Retrieve uses read_s with QuerySchema(getters={"pk": ...}).
  • Path PK schema type inferred from model PK via ModelUtil.pk_field_type.
  • Default read query data comes from ModelSerializer.QuerySet.read via query_util.
  • Built-ins and custom decorators composed with decorate_view (e.g., paginate, unique_view, extras).
  • ModelSerializer:
  • Binds util = ModelUtil(cls) and query_util = QueryUtil(cls) to subclasses.
  • queryset_request applies configured optimizations from QuerySet.queryset_request.
  • ModelUtil internals:
  • Unified _apply_query_optimizations merges explicit select/prefetch with auto-discovered relations when is_for_read=True.
  • Serialization paths standardized through internal helpers; read_s/list_read_s accept schema first.
  • Auth:
  • AsyncJwtBearer.verify_token simplifies error handling; drops explicit AuthError.
  • Imports:
  • ManyToManyAPI consumed from ninja_aio/helpers/api.py.
  • Runtime requirements:
  • Upper bounds added: django-ninja <=1.5.1, joserfc <=1.4.1, orjson <=3.11.5.
  • Docs and site:
  • MkDocs/mike integration for versioned docs; new workflow docs.yml.

🔴 Breaking Changes

  • Path PK schema type:
  • PK type is inferred from the model PK. Code relying on int | str in path schemas may need adjustments.
  • ManyToMany GET response shape:
  • Response changed from a plain list to {items: [...], count: N}. Clients must adapt parsing.
  • Import paths:
  • Schema helpers moved under ninja_aio/schemas/helpers.py and re-exported by ninja_aio/schemas/__init__.py.
  • ManyToManyAPI import is now from ninja_aio.helpers.api import ManyToManyAPI.
  • ModelUtil read API:
  • read_s and list_read_s signatures accept schema first and support instance or query_data. Code passing (request, obj, schema) must switch to (schema, request, instance=obj).

📝 Documentation

  • Updated:
  • ModelUtil reference: QuerySet config, QueryUtil, query schemas, get_objects, get_object, read_s, list_read_s.
  • APIViewSet: list/retrieve flow, PK type inference, M2M GET envelope, async/sync filter handlers, operation decorators.
  • Tutorial (model): QuerySet config and query_util examples; fetch/serialize using query schemas.
  • Index: overview of query optimizations and schemas.
  • ORJSON renderer: configuration guide.

⚠ Notes / Potential Impact

| Area | Observation | Impact | | ------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------- | | Query optimizations | is_for_read=True merges explicit and auto-discovered relations. | More joins/prefetches; re-check performance for heavy endpoints. | | Requirements caps | Upper bounds added for core deps. | Ensure compatible versions in your environment. | | Decorator order | decorate_view applies standard Python stacking order. | Verify nesting with paginate, unique_view, and custom decorators. |


🔍 Migration / Action

  1. Update imports:
  2. from ninja_aio.schemas.helpers import QuerySchema, ObjectQuerySchema, ObjectsQuerySchema, ModelQuerySetSchema, ModelQuerySetExtraSchema
  3. from ninja_aio.helpers.api import ManyToManyAPI
  4. Adjust M2M GET consumers to handle {items, count}.
  5. Update read_s/list_read_s calls to new parameter order.
  6. Verify path PK handling in custom routes that relied on a generic PK type.
  7. Review QuerySet.read / QuerySet.queryset_request for desired select/prefetch behavior.
  8. Optionally configure ORJSON via NINJA_AIO_ORJSON_RENDERER_OPTION.
v2.0.0-rc1
diff
2025-12-07notes
Release notes

Release Notes

[2.0.0-rc1] - 2025-12-07


✨ Added

  • QueryUtil and query scopes:
  • New QueryUtil with SCOPES (READ, QUERYSET_REQUEST, plus extras) and apply_queryset_optimizations.
  • ModelSerializer.query_util bound per model via __init_subclass__.
  • ModelSerializer.QuerySet supports read, queryset_request, extras.
  • Query schemas:
  • QuerySchema, ObjectQuerySchema, ObjectsQuerySchema, ModelQuerySetSchema, ModelQuerySetExtraSchema, QueryUtilBaseScopesSchema.
  • ModelUtil:
  • get_objects(...): optimized queryset fetching with filters and select/prefetch hints.
  • get_object(...): single-object retrieval by pk or getters with optimizations.
  • read_s(...) and list_read_s(...): serialize instances or auto-fetch via query schemas.
  • Relation discovery helpers: get_select_relateds(), get_reverse_relations().
  • PK type resolution: pk_field_type with helpful error for unknown field types.
  • ManyToManyAPI:
  • GET related endpoints return {items: [...], count: N}.
  • Relation filter handlers accept sync or async functions.
  • Related items use ModelUtil.list_read_s for serialization.
  • Schemas modularization:
  • New modules: ninja_aio/schemas/api.py, ninja_aio/schemas/generics.py, and exported names under ninja_aio/schemas/__init__.py.
  • Decorators:
  • Minor hardening and docs for aatomic and unique_view.

🛠 Changed

  • APIViewSet:
  • List view uses ModelUtil.get_objects and list_read_s with read optimizations; filter hooks retained.
  • Retrieve view uses read_s with QuerySchema(getters={"pk": ...}).
  • Path PK schema type inferred from model PK via ModelUtil.pk_field_type.
  • Default read query data comes from ModelSerializer.QuerySet.read via query_util.
  • ModelSerializer:
  • Binds util = ModelUtil(cls) and query_util = QueryUtil(cls) to subclasses.
  • queryset_request applies configured optimizations from QuerySet.queryset_request.
  • ModelUtil internals:
  • Unified _apply_query_optimizations merges explicit select/prefetch with auto-discovered relations when is_for_read=True.
  • Serialization paths standardized through internal bump helpers.
  • Auth:
  • AsyncJwtBearer.verify_token simplifies error handling; drops explicit AuthError.
  • Imports:
  • ManyToManyAPI consumed from ninja_aio/helpers/api.py.
  • Runtime requirements:
  • Pinned upper bounds for django-ninja, joserfc, orjson.

📝 Documentation

  • Updated:
  • ModelUtil reference: QuerySet config, QueryUtil, query schemas, get_objects, get_object, read_s, list_read_s.
  • APIViewSet: list/retrieve flow, PK type inference, M2M GET envelope, async/sync filter handlers.
  • Tutorial (model): QuerySet config and query_util examples; fetch/serialize using query schemas.
  • Index: overview of query optimizations and schemas.

🔴 Breaking Changes

  • Path PK schema type:
  • PK type is now inferred from the model PK. Code relying on int | str in path schemas may need adjustments.
  • ManyToMany GET response shape:
  • Response changed from a plain list to an envelope {items: [...], count: N}. Clients must adapt parsing.
  • Import paths:
  • Schema helpers moved under ninja_aio/schemas/helpers.py and re-exported by ninja_aio/schemas/__init__.py.
  • ManyToManyAPI import is now from ninja_aio.helpers.api import ManyToManyAPI.
  • ModelUtil read API:
  • read_s and list_read_s signatures accept schema first and support instance or query_data. Code passing (request, obj, schema) must switch to (schema, request, instance=obj).

⚠ Notes / Potential Impact

| Area | Observation | Impact | | ---- | ----------- | ------ | | Query optimizations | is_for_read=True merges explicit and auto-discovered relations. | More joins/prefetches; re-check performance for heavy endpoints. | | Requirements caps | Upper bounds added for core deps. | Ensure compatible versions in your environment. |


🔍 Migration / Action

  1. Update imports:
  2. from ninja_aio.schemas.helpers import QuerySchema, ObjectQuerySchema, ObjectsQuerySchema, ModelQuerySetSchema, ModelQuerySetExtraSchema
  3. from ninja_aio.helpers.api import ManyToManyAPI
  4. Adjust M2M GET consumers to handle {items, count}.
  5. Update read_s/list_read_s calls to new parameter order.
  6. Verify path PK handling in custom routes that relied on a generic PK type.
  7. Review QuerySet.read / QuerySet.queryset_request for desired select/prefetch behavior.

✅ Suggested Follow-Ups

  • Add perf checks around list/retrieve with merged relations.
  • Expand tests for:
  • PK type inference in path schemas.
  • Sync vs async relation filter handlers.
  • QueryUtil extras scopes resolution and application.

v2.0.0-rc2
diff
2025-12-12notes
Release notes

Release Notes

[2.0.0-rc2] - 2025-12-12


✨ Added

  • support for django-ninja 1.5.1
  • support for orjson 3.11.5
v2.0.0-rc3
diff
2025-12-12notes
Release notes

Release Notes

[2.0.0-rc3] - 2025-12-12


✨ Added

  • ManyToManyAPI:
  • New per-relation POST object resolution handler: <related_name>_query_handler(self, request, pk, instance) returning a queryset, resolved via .afirst().
  • Endpoint registration details documented: GET without trailing slash, POST with trailing slash; operationId conventions (get_{base}_{rel}, manage_{base}_{rel}).

🛠 Changed

  • ManyToManyAPI:
  • Split handlers: GET uses <related_name>_query_params_handler(self, queryset, filters_dict); POST uses <related_name>_query_handler(...) for per-PK validation.
  • Manage view uses _collect_m2m(...) with additional context (related_name, instance) and falls back to ModelUtil.get_objects(...) when query handler is absent.
  • Improved docs and docstrings for concurrency, error semantics, and request/response payloads.
  • Docs:
  • Refined M2M section: clarified handlers, paths, operationIds, request bodies, and concurrency.
  • Minor wording and formatting improvements; standardized examples.
  • Version:
  • Bump to 2.0.0-rc3.

📝 Documentation

  • APIViewSet M2M docs updated:
  • Clarified GET filters vs POST per-PK resolution.
  • Documented response semantics and per-PK success/error messages.
  • Added an example showcasing both handlers.

🔴 Breaking Changes

  • Handler naming:
  • GET filters must use <related_name>_query_params_handler; POST add/remove resolution must use <related_name>_query_handler. Existing single-handler implementations should be split accordingly.
  • Endpoint paths:
  • GET relation: /{base}/{pk}/{rel_path} (no trailing slash).
  • POST relation: /{base}/{pk}/{rel_path}/ (trailing slash).

⚠ Notes / Potential Impact

| Area | Observation | Impact | | ---- | ----------- | ------ | | Validation | POST uses per-PK resolution handler when present; fallback uses ModelUtil.get_objects. | Tighten access control and scoping per relation. | | Concurrency | aadd and aremove run concurrently via asyncio.gather. | Faster bulk mutations; ensure thread-safety of custom logic. |


🔍 Migration / Action

  1. Implement per-relation handlers:
  2. GET filters: def|async def <rel>_query_params_handler(self, qs, filters: dict) -> qs.
  3. POST resolution: async def <rel>_query_handler(self, request, pk, instance) -> queryset.
  4. Verify clients and OpenAPI consumers against the documented endpoint paths and operationIds.
  5. Ensure manage responses are consumed as documented (results, errors with count and details).

✅ Suggested Follow-Ups

  • Add tests for:
  • Presence/absence of <related_name>_query_handler fallback behavior.
  • Sync vs async GET filter handlers.
  • Per-PK error and success detail aggregation.
v2.0.0-rc4
diff
2025-12-12notes
Release notes

Release Notes

[2.0.0-rc4] - 2025-12-12


✨ Added

  • possibility to override router tag in APIViewSet

v2.0.0-rc5
diff
2025-12-12notes
Release notes

Release Notes

[2.0.0-rc5] - 2025-12-12


🛠 Changed

  • fix: update log messages to use 'pk' instead of 'id' for consistency in ManyToManyAPI

v2.0.0-rc6
diff
2025-12-12notes
Release notes

Release Notes

[2.0.0-rc6] - 2025-12-12


✨ Added

  • ORJSONRenderer:
  • Configurable orjson option via Django settings: NINJA_AIO_ORJSON_RENDERER_OPTION.
  • New dumps classmethod applying the configured option to all JSON responses.

🛠 Changed

  • Version bump:
  • __version__ updated from 2.0.0-rc5 to 2.0.0-rc6.
  • Rendering internals:
  • render now calls self.dumps(...) instead of orjson.dumps(...) directly.

📝 Documentation

  • Mention NINJA_AIO_ORJSON_RENDERER_OPTION in setup/config docs with example values (e.g., orjson.OPT_NAIVE_UTC, orjson.OPT_SERIALIZE_DATACLASS).

🔍 Migration / Action

  1. If you need specific JSON encoding behavior, set in Django settings:
  2. NINJA_AIO_ORJSON_RENDERER_OPTION = orjson.OPT_NAIVE_UTC | orjson.OPT_SERIALIZE_NUMPY (example).
  3. No code changes required for consumers; behavior is backward compatible when the setting is absent.

⚠ Notes / Potential Impact

| Area | Observation | Impact | | ---- | ----------- | ------ | | JSON options | Renderer honors global orjson options. | Unified behavior across endpoints; verify compatibility with clients. |


v2.0.0-rc7
diff
2025-12-16notes
Release notes

Release Notes

[2.0.0-rc7] - 2025-12-16


✨ Added

  • Decorators:
  • decorate_view: compose multiple decorators (sync/async), preserves normal stacking order, skips None.
  • APIViewSet.extra_decorators: declarative per-operation decorators.
  • DecoratorsSchema in ninja_aio.schemas.helpers to configure per-op decorators.

🛠 Changed

  • APIViewSet:
  • create, list, retrieve, update, delete compose built-ins (unique_view, paginate) and user-provided extras via decorate_view for consistent ordering.

📝 Documentation

  • New:
  • docs/api/views/decorators.md: decorate_view usage, conditional decoration, and extra_decorators with DecoratorsSchema.
  • docs/api/renderers/orjson_renderer.md: how to configure ORJSON options in settings.py.

⚠ Notes / Potential Impact

  • Decorator order:
  • decorate_view applies decorators in standard Python stacking semantics. If you relied on a specific nesting between paginate, unique_view, and custom decorators, verify behavior.

🔍 Migration / Action

  1. Optionally move per-operation decorators to APIViewSet.extra_decorators = DecoratorsSchema(...).
  2. If desired, configure ORJSON behavior via NINJA_AIO_ORJSON_RENDERER_OPTION in settings.py.
v1.0.5
diff
2025-12-07notes
Release notes

[1.0.5] - 2025-12-07

🛠 Changed

  • limited support up to django ninja 1.4.5
v1.0.4
diff
2025-11-03notes
Release notes

[1.0.4] - 2025-11-03


✨ Added

  • ModelUtil._rewrite_nested_foreign_keys: reintroduced helper to rename nested FK keys from <field> to <field>_id inside nested dicts (currently invoked conditionally in parse_output_data).

🛠 Changed

  • ModelUtil._extract_field_obj converted to async; now uses agetattr for safer async attribute access.
  • ModelUtil.parse_output_data:
  • Awaits the new async _extract_field_obj.
  • Fetches related instance first, then (conditionally) calls _rewrite_nested_foreign_keys when the outer field is a ForeignKey.

📝 Documentation

  • Table in docs/api/models/model_serializer.md (CreateSerializer attributes) reformatted:
  • Condensed multiline description for customs into a single line with semicolons.
  • Adjusted column widths / alignment for cleaner diff footprint.

⚠ Note / Potential Issue

| Area | Observation | Impact | | ---- | ----------- | ------ | | parse_output_data | Result of _rewrite_nested_foreign_keys is assigned to local v but not reattached to payload (final output still sets payload[k] = rel_instance). | FK key rewriting may be a no-op for consumers; behavior might not match intent. |


🔍 Migration / Action

  1. If you relied on the absence of FK key rewriting (1.0.3), verify whether the restored helper actually affects payloads (it likely does not yet).
  2. If rewriting is desired, ensure the transformed dict (or additional metadata) is surfaced in the serialized output or adjust logic accordingly.

✅ Suggested Follow-Ups

  • Add a test asserting expected presence (or absence) of <field>_id keys in nested output.
  • Decide whether payload should expose both the related object and rewritten key map, or deprecate the helper again if not needed.

v1.0.3
diff
2025-11-03notes
Release notes

[1.0.3] - 2025-11-03


✨ Added

  • M2MRelationSchema: New optional field related_schema documented (auto-generated when using a ModelSerializer).

🛠 Changed

  • Documentation tables (CRUD, Core Attributes, Auth, M2M Endpoints, Hooks) reformatted for alignment & readability.
  • Extra blank lines inserted to improve Markdown rendering clarity.
  • ModelUtil.parse_output_data: simplified nested relation handling (direct instance assignment).

🗑 Removed

  • ModelUtil._rewrite_nested_foreign_keys helper.
  • Foreign key nested dict rewriting logic (<field><field>_id) during output serialization.

📄 Documentation

  • Added warning block describing support for plain Django Model in M2MRelationSchema.model and mandatory related_schema when used.
  • Added related_schema bullet to M2M relation capabilities list.
  • Ensured file ends with a trailing newline.

⚠ Breaking Change

| Change | Impact | | ------ | ------ | | Removal of FK key rewriting in nested outputs | Clients expecting <nested_fk>_id keys must adjust parsing logic |


🔍 Migration Notes

  1. If consumers relied on <nested_fk>_id keys, add a post-serialization adapter to inject them, or reintroduce prior logic.
  2. When declaring M2M relations with plain Django models, always provide related_schema; omission now results in validation errors.

📌 Highlights

  • Cleaner docs + explicit M2M plain model guidance.
  • Leaner serialization path (less mutation, clearer intent).

🧪 Suggested Follow‑Ups

  • Add regression test ensuring nested FK dicts are no longer rewritten.
  • Consider exposing an optional flag to restore legacy FK key rewriting if demand appears.
v1.0.2
diff
2025-11-01notes
Release notes

[1.0.2] - 2025-11-01


✨ Added

  • SonarCloud Quality Gate badge (README + docs index).
  • Custom domain support (docs/CNAME).
  • Release Notes page with dynamic macros (docs/release_notes.md + mkdocs-macros-plugin).
  • Release automation script (main.py) generating tables, full changelog, and cards.
  • ManyToManyAPI helper (ninja_aio/helpers/api.py) with dynamic GET / ADD / REMOVE endpoints, filter schemas, concurrent operations, and query handler support.
  • Helpers package export (helpers/__init__.py).
  • Extended schema support in M2MRelationSchema (auto related_schema via validator).
  • Refactored M2M integration in APIViewSet (now uses ManyToManyAPI).
  • New test suites: decorators, exceptions/API, renderer/parser, many-to-many API.
  • Centralized literal for “not found” (tests/generics/literals.py).

🛠 Changed

  • NotFoundError: error key now uses underscored verbose name.
  • ORJSONRenderer: replaced nested mutation with recursive transform.
  • ModelUtil / ModelSerializer: added comprehensive docstrings, normalized custom field tuples, improved FK and nested output handling.
  • Removed inline M2M view logic from APIViewSet.
  • Enriched model serializer docs (tables, normalization, error cases).
  • M2MRelationSchema: validation for related schema generation.

🧾 Documentation

  • Major rewrite of docs/api/models/model_serializer.md: normalization workflow, error cases, best practices, expanded examples.
  • Added Release Notes navigation in mkdocs.yml.
  • Inline internal-use warning for ManyToManyAPI.
  • Improved readability (spacing, tables, JSON formatting).

✅ Tests

  • Coverage for:
  • ORJSON transformations (bytes→base64, IP→string).
  • unique_view name suffix logic.
  • Exception parsing and API defaults.
  • M2M add/remove flows + duplicate/error handling.
  • Updated NotFoundError key format.
  • Reused shared literal for 404 assertions.

📦 Tooling

  • Added mkdocs-macros-plugin.
  • Automated release visualization (HTML tables, cards).
  • Cleaner MkDocs theme (font configuration).

⚠ Impact

| Change | Potential Effect | | ------ | ---------------- | | Underscored error keys | Clients parsing old keys must adjust | | Extracted M2M logic | Custom subclasses relying on internals must migrate | | 2‑tuple customs now required | Missing values trigger validation errors |


🔍 Upgrade Notes

  1. Update error handling for new 404 key shape.
  2. Migrate any manual M2M endpoint wiring to ManyToManyAPI.
  3. Review custom field tuples—add defaults if optional behavior desired.

🧪 Follow‑Ups

  • Tag release (git tag -a vX.Y.Z -m "Release vX.Y.Z" && git push --tags).
  • Optionally add top-level CHANGELOG.md.
  • Decide on public stability of ManyToManyAPI (remove warning when ready).

📌 Release Template

```markdown

vX.Y.Z (YYYY-MM-DD)

Highlights: - ...

Full release table: /release_notes/

v1.0.1
diff
2025-10-30notes
Release notes

1.0.1 - 2025-10-30

Added

  • Docs: New dev dependencies file requirements.dev.txt.
  • MkDocs: Additional plugins (mkdocstrings, section-index, autorefs) and extended markdown_extensions.
  • Theme extras: social links, analytics stub, version metadata.
  • CSS: Logo sizing rules in docs/extra.css.

Changed

  • README: Reduced length, modernized intro, added concise feature + quick start sections.
  • Pagination docs: Reformatted tables, spacing, clarified examples.
  • Contributing docs: Expanded with setup, PR guidelines, issue template hints.
  • Tutorial (CRUD & Filtering): Table formatting, spacing normalization, improved examples.
  • Favicon path moved docs/img/favicon.icodocs/images/favicon.ico; logo updated.
  • Index docs: Documentation URL switched to custom domain.
  • MkDocs config:
  • site_url updated to https://django-ninja-aio.com/.
  • Added logo/favicon references and rich navigation features.
  • Expanded palette + features (search, code copy/select, tooltips, etc.).
  • PyProject metadata: Documentation URL updated to new domain.
  • Pagination imports switched to from ninja.pagination instead of local alias in examples.
  • Refactor: _m2m_views now takes a single M2MRelationSchema and is invoked in a loop (improves clarity).
  • Minor docstring spacing added before CRUD endpoint decorators.
  • M2M registration: Logic unchanged functionally but simplified iteration pattern.

Removed

  • Legacy automatic loop inside _m2m_views (replaced by external loop in _add_views).
  • Redundant long README sections (old serializer deep examples, extended auth/pagination prose).

Internal

  • _add_views now iterates self.m2m_relations and calls _m2m_views(relation) for each.
  • Consistent path/auth resolution maintained; no schema changes to public API.
  • Added use_directory_urls: true explicitly in mkdocs.yml.

Impact

  • No breaking API changes.
  • Documentation structure improved; search indexing benefits from new plugins.
  • M2M internals slightly cleaner; external behavior stable.

Migration Notes

No action required for existing users.

v1.0.0
diff
2025-10-28notes
Release notes

1.0.0 - 2025-10-28

Added

  • Per‑relation M2M configuration via M2MRelationSchema (replaces tuples).
  • Per‑relation flags: add, remove, get.
  • Per‑relation filters with dynamic schema generation and hook <related_name>_query_params_handler.
  • Method _generate_m2m_filters_schemas to build all M2M filter schemas.
  • Query param injection for M2M GET: filters: Query[filters_schema] = None.
  • Extended docstrings for APIViewSet and internal helper methods.
  • Overridable hooks documented in docs (query_params_handler, per‑relation handlers).
  • Changelog: version bump to __version__ = "1.0.0".

Changed

  • api_view_set.md rewritten: tuple-based M2M section replaced with M2MRelationSchema docs, new sections for filters, hooks, examples.
  • CRUD table wording (schema_out formatting, notes clarified).
  • Auth resolution notes now include M2M fallback logic.
  • Internal view registration: per-relation flags extracted (m2m_add/remove/get replaced by schema attributes).
  • Error message spacing adjusted in _check_m2m_objs.
  • Refactored internal function docs (more concise, purpose-focused).
  • Dynamic filter/path schemas built through unified _generate_schema.

Removed

  • Class attributes: m2m_add, m2m_remove, m2m_get.
  • Tuple-based m2m_relations formats.
  • Legacy verbose examples inside views() docstring.
  • Redundant m2m_auth entry in auth table (moved to core attributes table).

Internal

  • Added per-method docstrings (create_view, list_view, retrieve_view, update_view, delete_view, _m2m_views, _add_views, etc.).
  • _crud_views now described as a mapping.
  • Added storage of self.m2m_filters_schemas during init.
  • GET M2M handler applies optional per-relation filter hook if present.
  • Manage M2M handler chooses input schema dynamically (M2MSchemaIn / M2MAddSchemaIn / M2MRemoveSchemaIn).

Migration Notes

Old: python m2m_relations = [ (Tag, "tags"), (Category, "categories", "article-categories"), (Author, "authors", "co-authors", [AdminAuth()]) ] m2m_add = True m2m_remove = True m2m_get = True

New: ```python from ninja_aio.schemas import M2MRelationSchema

m2m_relations = [ M2MRelationSchema(model=Tag, related_name="tags"), M2MRelationSchema(model=Category, related_name="categories", path="article-categories"), M2MRelationSchema(model=Author, related_name="authors", path="co-authors", auth=[AdminAuth()]) ]

Disable ops per relation if needed:

M2MRelationSchema(model=Tag, related_name="tags", add=False, remove=False, get=True)

```

Per‑relation filters: ```python M2MRelationSchema( model=Tag, related_name="tags", filters={"name": (str, "")} )

async def tags_query_params_handler(self, queryset, filters): if filters.get("name"): queryset = queryset.filter(name__icontains=filters["name"]) return queryset ```

Breaking Changes

  • m2m_relations must use M2MRelationSchema (no tuples).
  • Removed m2m_add, m2m_remove, m2m_get (use per-relation flags).
  • Any code unpacking relation tuples must be updated to attribute access.

Summary

Release focuses on granular M2M configuration, per‑relation filtering, cleaner internals, and clearer documentation for extensibility.

v0.11.4
diff
2025-10-28notes
Release notes

0.11.4 - 2025-10-28

Changed

  • Documentation heading renamed from # API ViewSet to # APIViewSet.
  • Docs rewritten: long examples replaced with concise endpoint table and structured attribute sections.
  • Core attributes table expanded (added pagination_class, query_params, disable, endpoint doc strings).
  • Clarified authentication resolution; explicit mention of m2m_auth.

Added

  • Per-relation M2M configuration: support for 3- and 4-element tuples in m2m_relations.
  • 3 elements: (model, related_name, custom_path)
  • 4 elements: (model, related_name, custom_path, per_relation_auth)
  • Per-relation auth override (local m2m_auth inside _m2m_views loop).
  • Documentation of M2M path/auth resolution rules.

Removed

  • Global m2m_path attribute (replaced by per-relation path tuple element).
  • Old m2m_relations signature list[tuple[ModelSerializer | Model, str]].

Internal Implementation

  • M2M loop updated: for m2m_data in self.m2m_relations: with dynamic tuple length parsing.
  • Path resolution: python rel_path = rel_util.verbose_name_path_resolver() if not m2m_path else m2m_path
  • Auth passed to decorators as auth=m2m_auth instead of auth=self.m2m_auth.
  • Continued use of @unique_view(...) for stable handler naming.

Migration Notes

```python

Before

m2m_relations = [(Tag, "tags")] m2m_path = "custom-tags" # no longer supported

After

m2m_relations = [ (Tag, "tags"), # auto path + fallback auth (Category, "categories", "custom-categories"), # custom path (Author, "authors", "article-authors", [AdminAuth()]) # custom path + custom auth ] `` - Remove anym2m_path` usage. - 2-element tuples remain valid (no breaking change).

Summary

Improved flexibility and granularity for M2M relation configuration and streamlined documentation.

v0.11.3
diff
2025-10-28notes
Release notes

[0.11.3] - 2025-10-28

Added

  • M2M Path Customization: Added m2m_path attribute to APIViewSet for custom many-to-many relationship endpoint paths
  • Default: empty string (uses auto-generated path from model verbose name)
  • Allows overriding the default path resolution for M2M endpoints

Changed

APIViewSet Class Attributes

  • m2m_relations type annotation: Changed from tuple[ModelSerializer | Model, str] to list[tuple[ModelSerializer | Model, str]]
  • More flexible and mutable data structure
  • Allows dynamic modification of M2M relations at runtime

Code Quality & Formatting

  • Consistent blank lines: Added blank lines after function returns for better code readability
  • Applied to: create_view(), list_view(), retrieve_view(), update_view(), delete_view()
  • Removed extra blank line: Cleaned up unnecessary blank line in delete_view() method
  • M2M views refactoring: Improved code structure for many-to-many relationship views
  • Applied @unique_view decorator to M2M endpoints (get_related, manage_related)
  • Removed manual __name__ assignment in favor of decorator pattern
  • Better separation of concerns between GET and POST operations
  • Moved conditional M2M add/remove logic outside of the GET endpoint block

M2M Endpoint Generation

  • Dynamic path resolution: M2M endpoints now respect custom m2m_path attribute ```python rel_path = ( rel_util.verbose_name_path_resolver() if not self.m2m_path else self.m2m_path )
v0.11.1
diff
2025-10-28notes
Release notes

[0.11.1] - 2025-10-28

Fixed

  • Fixed typo in module name: renamed decoratos.py to decorators.py
  • Updated import statement in views.py to use correct decorators module name

Changed

Documentation

  • Homepage Examples - Updated traditional approach comparison
  • Changed from Django REST Framework serializers to Django Ninja ModelSchema
  • Simplified example from UserSerializer to UserSchemaOut
  • Simplified example from UserCreateSerializer to UserSchemaIn
  • Updated view examples to use Django Ninja's @api.get() and @api.post() decorators
  • Replaced class-based views (UserListView, UserCreateView) with function-based views
  • Removed sync_to_async wrapper calls in favor of native async Django ORM operations
  • Simplified user creation with direct acreate() usage
  • Updated response format to use tuple-based status code returns (201, user)
  • Made code examples more concise and modern

Technical Details

Module Renaming

```python

Before (v0.11.0)

from .decoratos import unique_view

After (v0.11.1)

from .decorators import unique_view

v0.11.0
diff
2025-10-26notes
Release notes

[0.11.0] - 2025-10-26

Added

Documentation

  • Complete documentation website with MkDocs Material theme
  • Custom domain configuration (ninja-aio.dev) via CNAME
  • Getting Started Guide
  • Installation instructions
  • Quick start tutorial with screenshots
  • Auto-generated Swagger UI examples
  • Tutorial Series (4 comprehensive steps)
  • Step 1: Define Your Model - Complete guide to ModelSerializer with relationships, custom fields, and lifecycle hooks
  • Step 2: Create CRUD Views - APIViewSet usage, custom endpoints, query parameters, and error handling
  • Step 3: Add Authentication - JWT setup with RSA keys, role-based access control, and ownership validation
  • Step 4: Add Filtering & Pagination - Advanced filtering, full-text search, ordering, and performance optimization
  • API Reference Documentation
  • Authentication guide (965 lines) covering AsyncJwtBearer, JWT validation, RBAC, and integrations
  • ModelSerializer reference (806 lines) with schema generation and relationship handling
  • ModelUtil reference (1,066 lines) detailing CRUD operations and data transformations
  • APIView documentation for custom endpoints
  • APIViewSet documentation (327 lines) for complete CRUD operations
  • Pagination guide (750 lines) with custom pagination examples
  • Contributing guidelines
  • Logo and branding assets
  • Extra CSS styling for code blocks

Core Features

  • NotFoundError Exception
  • New exception class for 404 errors with model-aware error messages
  • Automatically includes model verbose name in error response
  • Status code 404 with structured error format

Utilities

  • Decorators Module (ninja_aio/decoratos.py)
  • aatomic decorator for asynchronous atomic transactions
  • AsyncAtomicContextManager for async transaction context management
  • unique_view decorator for generating unique view names based on model metadata
  • Support for both singular and plural model naming conventions

Examples

  • Example 1 (examples/ex_1/)
  • Basic User model without relationships
  • Simple ViewSet implementation
  • Basic URL configuration
  • Example 2 (examples/ex_2/)
  • User and Customer models with ForeignKey relationship
  • JWT authentication setup with RSA keys
  • Complete auth configuration with mandatory claims
  • Related field serialization examples

Development Tools

  • MkDocs configuration (mkdocs.yml)
  • Material theme with deep purple color scheme
  • Dark/light mode support with auto-detection
  • Navigation tabs and integrated TOC
  • Code highlighting with Pygments
  • Admonitions and superfences support
  • Documentation requirements file
  • Custom CSS for documentation styling

Changed

Core Models

  • ModelSerializer
  • Enhanced docstring (113 lines) with comprehensive API documentation
  • Detailed explanation of schema generation and relationship handling
  • Examples for CreateSerializer, ReadSerializer, and UpdateSerializer
  • Documented sync and async lifecycle hooks
  • ModelUtil
  • Enhanced docstring (79 lines) documenting all CRUD operations
  • Detailed method documentation for parse_input_data, parse_output_data, and CRUD methods
  • Performance notes and error handling documentation
  • Updated to use NotFoundError instead of generic SerializeError for 404 cases

Views

  • APIViewSet
  • Applied @unique_view decorator to all generated CRUD methods (create, list, retrieve, update, delete)
  • Removed manual __name__ assignment in favor of decorator-based approach
  • Cleaner method definitions without post-definition name mutations
  • APIView
  • Added comprehensive docstring explaining base class functionality

Authentication

  • AsyncJwtBearer
  • Enhanced docstring (71 lines) with detailed attribute and method documentation
  • Security considerations and best practices
  • Integration examples with Auth0, Keycloak, and Firebase

Project Structure

  • Reorganized documentation structure with clear separation of concerns

Fixed

  • Consistent error handling using NotFoundError for object not found scenarios
  • Proper async context management for database transactions

Documentation Improvements

Tutorial Content

  • 4,435 total lines of tutorial content
  • 120+ code examples across all tutorials
  • 50+ API usage examples with curl commands
  • Comprehensive error handling examples
  • Performance optimization tips and best practices

API Reference

  • 3,994 total lines of API reference documentation
  • Complete method signatures with parameter descriptions
  • Return type documentation
  • Error handling specifications
  • Integration examples

Visual Assets

  • Swagger UI screenshots for all CRUD operations
  • Logo and branding images
  • Diagram examples (where applicable)

Notes

Breaking Changes

None - This is a documentation and enhancement release

Migration Required

None - All changes are backward compatible

Known Issues

None reported

Links

  • Documentation: https://caspel26.github.io/django-ninja-aio-crud/
v0.10.3
diff
2025-09-23notes
Release notes

[0.10.3] - 2025-09-23

🔧 Changed

  • ModelUtil Refactoring: Extracted model field handling logic into separate property
  • Added model_fields property to encapsulate [field.name for field in self.model._meta.get_fields()]
  • Updated serializable_fields property to use new model_fields property for non-ModelSerializerMeta models

🛠️ Fixed

  • Custom Field Filtering: Enhanced custom field detection logic to prevent conflicts with actual model fields
  • Custom fields are now filtered to exclude fields that exist in the actual Django model
  • Added k not in self.model_fields condition to both custom field dictionary comprehension and iteration logic
  • Prevents custom serializer fields from overriding or conflicting with real model fields

📈 Improvements

  • Code Organization: Better separation of concerns with dedicated model_fields property
  • Field Conflict Prevention: More robust handling of custom vs model field distinction
  • Code Readability: Improved maintainability by reducing code duplication in field name extraction

🔄 Technical Details

  • The customs dictionary now only includes truly custom fields that don't exist on the model
  • Custom field processing in the main loop now respects model field boundaries
  • Better encapsulation of model introspection logic
v0.10.2
diff
2025-09-18-
Release notes

No release notes.

v0.10.1
diff
2025-09-18-
Release notes

No release notes.

v0.10.0
diff
2025-09-15-
Release notes

No release notes.

0.9.2
diff
2025-08-25-
Release notes

No release notes.

v0.9.1
diff
2025-08-25-
Release notes

No release notes.

v0.9.0
diff
2025-07-18-
Release notes

No release notes.

v0.8.4
diff
2025-06-20-
Release notes

No release notes.

v0.8.3
diff
2025-06-18-
Release notes

No release notes.

v0.8.2
diff
2025-06-18-
Release notes

No release notes.

v0.8.1
diff
2025-06-18-
Release notes

No release notes.

v0.8.0
diff
2025-05-15-
Release notes

No release notes.

v0.7.8
diff
2025-03-21-
Release notes

No release notes.

v0.7.7
diff
2025-03-05-
Release notes

No release notes.

v0.7.6
diff
2025-02-24-
Release notes

No release notes.

v0.7.5
diff
2025-02-22-
Release notes

No release notes.

v0.7.4
diff
2025-02-20-
Release notes

No release notes.

v0.7.3
diff
2025-02-19-
Release notes

No release notes.

v0.7.2
diff
2025-01-30-
Release notes

No release notes.

v0.7.1
diff
2025-01-29-
Release notes

No release notes.

v0.7.0
diff
2025-01-29-
Release notes

No release notes.

v0.6.4
diff
2025-01-22-
Release notes

No release notes.

v0.6.3
diff
2025-01-22-
Release notes

No release notes.

v0.6.2
diff
2025-01-19-
Release notes

No release notes.

v0.6.1
diff
2025-01-13-
Release notes

No release notes.

v0.6.0
diff
2025-01-12-
Release notes

No release notes.

v0.5.0
diff
2025-01-09-
Release notes

No release notes.

v0.4.0
diff
2025-01-08-
Release notes

No release notes.

v0.3.1
diff
2024-11-07-
Release notes

No release notes.

v0.3.0
diff
2024-10-10-
Release notes

No release notes.

v0.2.2
diff
2024-10-03-
Release notes

No release notes.

v0.2.1
diff
2024-10-02-
Release notes

No release notes.

v0.2.0
diff
2024-10-01-
Release notes

No release notes.

v0.1.4
diff
2024-09-29-
Release notes

No release notes.

v0.1.3
diff
2024-09-28-
Release notes

No release notes.

v0.1.2
diff
2024-09-26-
Release notes

No release notes.

v0.1.1
diff
2024-09-26-
Release notes

No release notes.