Skip to content

Commit

Permalink
Added DI support for Model Service for ModelController
Browse files Browse the repository at this point in the history
  • Loading branch information
eadwinCode committed Jan 9, 2025
1 parent 12af6ff commit 8403c03
Show file tree
Hide file tree
Showing 14 changed files with 1,049 additions and 29 deletions.
129 changes: 129 additions & 0 deletions docs/api_controller/model_controller/01_getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Getting Started with Model Controllers

Model Controllers in Ninja Extra provide a powerful way to automatically generate CRUD (Create, Read, Update, Delete) operations for Django ORM models. They simplify API development by handling common database operations while remaining highly customizable.

## **Installation**

First, ensure you have Ninja Extra and ninja-schema installed:

```bash
pip install django-ninja-extra ninja-schema
```

`ninja-schema` package is optional, but it's recommended for generating schemas.

## **Basic Usage**

Let's start with a simple example. Consider this Django model:

```python
from django.db import models

class Category(models.Model):
title = models.CharField(max_length=100)

class Event(models.Model):
title = models.CharField(max_length=100)
category = models.OneToOneField(
Category, null=True, blank=True,
on_delete=models.SET_NULL,
related_name='events'
)
start_date = models.DateField()
end_date = models.DateField()

def __str__(self):
return self.title
```

To create a basic Model Controller for the Event model:

```python
from ninja_extra import (
ModelConfig,
ModelControllerBase,
api_controller,
NinjaExtraAPI
)
from .models import Event

@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
)

# Register the controller with your API
api = NinjaExtraAPI()
api.register_controllers(EventModelController)
```

This simple setup automatically creates the following endpoints:

- `POST /events/` - Create a new event
- `GET /events/{id}` - Retrieve a specific event
- `PUT /events/{id}` - Update an event
- `PATCH /events/{id}` - Partially update an event
- `DELETE /events/{id}` - Delete an event
- `GET /events/` - List all events (with pagination)

It is important to that if `model_config.model` is not set, the controller becomes a regular NinjaExtra controller.

## **Generated Schemas**

The Model Controller automatically generates Pydantic schemas for your model using `ninja-schema`. These schemas handle:

- Input validation
- Output serialization
- Automatic documentation in the OpenAPI schema

For example, the generated schemas for our `Event` model would look like this:

```python
# Auto-generated create/update schema
class EventCreateSchema(Schema):
title: str
start_date: date
end_date: date
category: Optional[int] = None

# Auto-generated retrieve schema
class EventSchema(Schema):
id: int
title: str
start_date: date
end_date: date
category: Optional[int] = None
```

## **Customizing Routes**

You can control which routes are generated using the `allowed_routes` parameter:

```python
@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
allowed_routes=["list", "find_one"] # Only generate GET and GET/{id} endpoints
)
```

## **Async Support**

Model Controllers support `async` operations out of the box. Just set `async_routes=True`:

```python
@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
async_routes=True # Enable async routes
)
```

## **Next Steps**

- Learn about [Model Configuration](02_model_configuration.md) for detailed schema and route customization
- Explore [Model Services](03_model_service.md) for customizing CRUD operations
- See how to use [Query and Path Parameters](04_parameters.md) effectively with `ModelEndpointFactory`
224 changes: 224 additions & 0 deletions docs/api_controller/model_controller/02_model_configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# Model Configuration

The `ModelConfig` class in Ninja Extra provides extensive configuration options for Model Controllers. It allows you to customize schema generation, route behavior, and pagination settings.

## **Basic Configuration**

Here's a comprehensive example of `ModelConfig` usage:

```python
from ninja_extra import (
ModelConfig,
ModelControllerBase,
ModelSchemaConfig,
api_controller,
)
from .models import Event

@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
schema_config=ModelSchemaConfig(
read_only_fields=["id", "created_at"],
write_only_fields=["password"],
include=["title", "start_date", "end_date", "category"],
exclude=set(), # Fields to exclude
depth=1, # Nesting depth for related fields
),
async_routes=False, # Enable/disable async routes
allowed_routes=["create", "find_one", "update", "patch", "delete", "list"],
)
```

## **Schema Configuration**

The `ModelSchemaConfig` class controls how Pydantic schemas are generated from your Django models:

```python
from ninja_extra import ModelConfig, ModelSchemaConfig

# Detailed schema configuration
schema_config = ModelSchemaConfig(
# Include specific fields (use "__all__" for all fields)
include=["title", "description", "start_date"],

# Exclude specific fields
exclude={"internal_notes", "secret_key"},

# Fields that should be read-only (excluded from create/update schemas)
read_only_fields=["id", "created_at", "updated_at"],

# Fields that should be write-only (excluded from retrieve schemas)
write_only_fields=["password"],

# Depth of relationship traversal
depth=1,

# Additional Pydantic config options
extra_config_dict={
"title": "EventSchema",
"description": "Schema for Event model",
"populate_by_name": True
}
)

model_config = ModelConfig(
model=Event,
schema_config=schema_config
)
```

## **Custom Schemas**

You can provide your own Pydantic schemas instead of using auto-generated ones:

```python
from datetime import date
from pydantic import BaseModel, Field

class EventCreateSchema(BaseModel):
title: str = Field(..., max_length=100)
start_date: date
end_date: date
category_id: int | None = None

class EventRetrieveSchema(BaseModel):
id: int
title: str
start_date: date
end_date: date
category_id: int | None

@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
create_schema=EventCreateSchema,
retrieve_schema=EventRetrieveSchema,
update_schema=EventCreateSchema, # Reuse create schema for updates
)
```

## **Pagination Configuration**

Model Controllers support customizable pagination for list endpoints:

```python
from ninja.pagination import LimitOffsetPagination
from ninja_extra import (
ModelConfig,
ModelPagination
)
from ninja_extra.pagination import NinjaPaginationResponseSchema

@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
# Configure pagination
pagination=ModelPagination(
klass=LimitOffsetPagination,
pagination_schema=NinjaPaginationResponseSchema,
paginator_kwargs={
"limit": 20,
"offset": 100
}
)
)
```

## **Route Configuration**

You can customize individual route behavior using route info dictionaries:

```python
@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
# Customize specific route configurations
create_route_info={
"summary": "Create a new event",
"description": "Creates a new event with the provided data",
"tags": ["events"],
"deprecated": False,
},
list_route_info={
"summary": "List all events",
"description": "Retrieves a paginated list of all events",
"tags": ["events"],
},
find_one_route_info={
"summary": "Get event details",
"description": "Retrieves details of a specific event",
"tags": ["events"],
}
)
```

## **Async Routes Configuration**

Enable async routes and configure async behavior:

```python
@api_controller("/events")
class AsyncEventModelController(ModelControllerBase):
model_config = ModelConfig(
model=Event,
# Async-specific configurations
async_routes=True,
schema_config=ModelSchemaConfig(
read_only_fields=["id"],
depth=1
)
)

# Custom async service implementation
service = AsyncEventModelService(model=Event)
```

## **Configuration Inheritance**

ModelConfig also support configuration inheritance:

```python
from ninja_extra.controllers import ModelConfig

class BaseModelConfig(ModelConfig):
async_routes = True
schema_config = ModelSchemaConfig(
read_only_fields=["id", "created_at", "updated_at"],
depth=1
)

@api_controller("/events")
class EventModelController(ModelControllerBase):
model_config = BaseModelConfig(
model=Event,
# Override or extend base configuration
allowed_routes=["list", "find_one"]
)
```

## **Best Practices**

1. **Schema Configuration**:
- Always specify `read_only_fields` for auto-generated fields
- Use `depth` carefully as it can impact performance
- Consider using `exclude` for sensitive fields

2. **Route Configuration**:
- Limit `allowed_routes` to only necessary endpoints
- Provide meaningful summaries and descriptions
- Use tags for API organization

3. **Pagination**:
- Always set reasonable limits
- Consider your data size when choosing pagination class
- Use appropriate page sizes for your use case

4. **Async Support**:
- Enable `async_routes` when using async database operations
- Implement custom async services for complex operations
- Consider performance implications of async operations
Loading

0 comments on commit 8403c03

Please sign in to comment.