Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ModelService Dependency Injection Support #222

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading