Skip to content

Commit c776235

Browse files
authored
Merge pull request #222 from eadwinCode/model_service
feat: ModelService Dependency Injection Support
2 parents 12af6ff + 8403c03 commit c776235

File tree

14 files changed

+1049
-29
lines changed

14 files changed

+1049
-29
lines changed
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Getting Started with Model Controllers
2+
3+
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.
4+
5+
## **Installation**
6+
7+
First, ensure you have Ninja Extra and ninja-schema installed:
8+
9+
```bash
10+
pip install django-ninja-extra ninja-schema
11+
```
12+
13+
`ninja-schema` package is optional, but it's recommended for generating schemas.
14+
15+
## **Basic Usage**
16+
17+
Let's start with a simple example. Consider this Django model:
18+
19+
```python
20+
from django.db import models
21+
22+
class Category(models.Model):
23+
title = models.CharField(max_length=100)
24+
25+
class Event(models.Model):
26+
title = models.CharField(max_length=100)
27+
category = models.OneToOneField(
28+
Category, null=True, blank=True,
29+
on_delete=models.SET_NULL,
30+
related_name='events'
31+
)
32+
start_date = models.DateField()
33+
end_date = models.DateField()
34+
35+
def __str__(self):
36+
return self.title
37+
```
38+
39+
To create a basic Model Controller for the Event model:
40+
41+
```python
42+
from ninja_extra import (
43+
ModelConfig,
44+
ModelControllerBase,
45+
api_controller,
46+
NinjaExtraAPI
47+
)
48+
from .models import Event
49+
50+
@api_controller("/events")
51+
class EventModelController(ModelControllerBase):
52+
model_config = ModelConfig(
53+
model=Event,
54+
)
55+
56+
# Register the controller with your API
57+
api = NinjaExtraAPI()
58+
api.register_controllers(EventModelController)
59+
```
60+
61+
This simple setup automatically creates the following endpoints:
62+
63+
- `POST /events/` - Create a new event
64+
- `GET /events/{id}` - Retrieve a specific event
65+
- `PUT /events/{id}` - Update an event
66+
- `PATCH /events/{id}` - Partially update an event
67+
- `DELETE /events/{id}` - Delete an event
68+
- `GET /events/` - List all events (with pagination)
69+
70+
It is important to that if `model_config.model` is not set, the controller becomes a regular NinjaExtra controller.
71+
72+
## **Generated Schemas**
73+
74+
The Model Controller automatically generates Pydantic schemas for your model using `ninja-schema`. These schemas handle:
75+
76+
- Input validation
77+
- Output serialization
78+
- Automatic documentation in the OpenAPI schema
79+
80+
For example, the generated schemas for our `Event` model would look like this:
81+
82+
```python
83+
# Auto-generated create/update schema
84+
class EventCreateSchema(Schema):
85+
title: str
86+
start_date: date
87+
end_date: date
88+
category: Optional[int] = None
89+
90+
# Auto-generated retrieve schema
91+
class EventSchema(Schema):
92+
id: int
93+
title: str
94+
start_date: date
95+
end_date: date
96+
category: Optional[int] = None
97+
```
98+
99+
## **Customizing Routes**
100+
101+
You can control which routes are generated using the `allowed_routes` parameter:
102+
103+
```python
104+
@api_controller("/events")
105+
class EventModelController(ModelControllerBase):
106+
model_config = ModelConfig(
107+
model=Event,
108+
allowed_routes=["list", "find_one"] # Only generate GET and GET/{id} endpoints
109+
)
110+
```
111+
112+
## **Async Support**
113+
114+
Model Controllers support `async` operations out of the box. Just set `async_routes=True`:
115+
116+
```python
117+
@api_controller("/events")
118+
class EventModelController(ModelControllerBase):
119+
model_config = ModelConfig(
120+
model=Event,
121+
async_routes=True # Enable async routes
122+
)
123+
```
124+
125+
## **Next Steps**
126+
127+
- Learn about [Model Configuration](02_model_configuration.md) for detailed schema and route customization
128+
- Explore [Model Services](03_model_service.md) for customizing CRUD operations
129+
- See how to use [Query and Path Parameters](04_parameters.md) effectively with `ModelEndpointFactory`
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
# Model Configuration
2+
3+
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.
4+
5+
## **Basic Configuration**
6+
7+
Here's a comprehensive example of `ModelConfig` usage:
8+
9+
```python
10+
from ninja_extra import (
11+
ModelConfig,
12+
ModelControllerBase,
13+
ModelSchemaConfig,
14+
api_controller,
15+
)
16+
from .models import Event
17+
18+
@api_controller("/events")
19+
class EventModelController(ModelControllerBase):
20+
model_config = ModelConfig(
21+
model=Event,
22+
schema_config=ModelSchemaConfig(
23+
read_only_fields=["id", "created_at"],
24+
write_only_fields=["password"],
25+
include=["title", "start_date", "end_date", "category"],
26+
exclude=set(), # Fields to exclude
27+
depth=1, # Nesting depth for related fields
28+
),
29+
async_routes=False, # Enable/disable async routes
30+
allowed_routes=["create", "find_one", "update", "patch", "delete", "list"],
31+
)
32+
```
33+
34+
## **Schema Configuration**
35+
36+
The `ModelSchemaConfig` class controls how Pydantic schemas are generated from your Django models:
37+
38+
```python
39+
from ninja_extra import ModelConfig, ModelSchemaConfig
40+
41+
# Detailed schema configuration
42+
schema_config = ModelSchemaConfig(
43+
# Include specific fields (use "__all__" for all fields)
44+
include=["title", "description", "start_date"],
45+
46+
# Exclude specific fields
47+
exclude={"internal_notes", "secret_key"},
48+
49+
# Fields that should be read-only (excluded from create/update schemas)
50+
read_only_fields=["id", "created_at", "updated_at"],
51+
52+
# Fields that should be write-only (excluded from retrieve schemas)
53+
write_only_fields=["password"],
54+
55+
# Depth of relationship traversal
56+
depth=1,
57+
58+
# Additional Pydantic config options
59+
extra_config_dict={
60+
"title": "EventSchema",
61+
"description": "Schema for Event model",
62+
"populate_by_name": True
63+
}
64+
)
65+
66+
model_config = ModelConfig(
67+
model=Event,
68+
schema_config=schema_config
69+
)
70+
```
71+
72+
## **Custom Schemas**
73+
74+
You can provide your own Pydantic schemas instead of using auto-generated ones:
75+
76+
```python
77+
from datetime import date
78+
from pydantic import BaseModel, Field
79+
80+
class EventCreateSchema(BaseModel):
81+
title: str = Field(..., max_length=100)
82+
start_date: date
83+
end_date: date
84+
category_id: int | None = None
85+
86+
class EventRetrieveSchema(BaseModel):
87+
id: int
88+
title: str
89+
start_date: date
90+
end_date: date
91+
category_id: int | None
92+
93+
@api_controller("/events")
94+
class EventModelController(ModelControllerBase):
95+
model_config = ModelConfig(
96+
model=Event,
97+
create_schema=EventCreateSchema,
98+
retrieve_schema=EventRetrieveSchema,
99+
update_schema=EventCreateSchema, # Reuse create schema for updates
100+
)
101+
```
102+
103+
## **Pagination Configuration**
104+
105+
Model Controllers support customizable pagination for list endpoints:
106+
107+
```python
108+
from ninja.pagination import LimitOffsetPagination
109+
from ninja_extra import (
110+
ModelConfig,
111+
ModelPagination
112+
)
113+
from ninja_extra.pagination import NinjaPaginationResponseSchema
114+
115+
@api_controller("/events")
116+
class EventModelController(ModelControllerBase):
117+
model_config = ModelConfig(
118+
model=Event,
119+
# Configure pagination
120+
pagination=ModelPagination(
121+
klass=LimitOffsetPagination,
122+
pagination_schema=NinjaPaginationResponseSchema,
123+
paginator_kwargs={
124+
"limit": 20,
125+
"offset": 100
126+
}
127+
)
128+
)
129+
```
130+
131+
## **Route Configuration**
132+
133+
You can customize individual route behavior using route info dictionaries:
134+
135+
```python
136+
@api_controller("/events")
137+
class EventModelController(ModelControllerBase):
138+
model_config = ModelConfig(
139+
model=Event,
140+
# Customize specific route configurations
141+
create_route_info={
142+
"summary": "Create a new event",
143+
"description": "Creates a new event with the provided data",
144+
"tags": ["events"],
145+
"deprecated": False,
146+
},
147+
list_route_info={
148+
"summary": "List all events",
149+
"description": "Retrieves a paginated list of all events",
150+
"tags": ["events"],
151+
},
152+
find_one_route_info={
153+
"summary": "Get event details",
154+
"description": "Retrieves details of a specific event",
155+
"tags": ["events"],
156+
}
157+
)
158+
```
159+
160+
## **Async Routes Configuration**
161+
162+
Enable async routes and configure async behavior:
163+
164+
```python
165+
@api_controller("/events")
166+
class AsyncEventModelController(ModelControllerBase):
167+
model_config = ModelConfig(
168+
model=Event,
169+
# Async-specific configurations
170+
async_routes=True,
171+
schema_config=ModelSchemaConfig(
172+
read_only_fields=["id"],
173+
depth=1
174+
)
175+
)
176+
177+
# Custom async service implementation
178+
service = AsyncEventModelService(model=Event)
179+
```
180+
181+
## **Configuration Inheritance**
182+
183+
ModelConfig also support configuration inheritance:
184+
185+
```python
186+
from ninja_extra.controllers import ModelConfig
187+
188+
class BaseModelConfig(ModelConfig):
189+
async_routes = True
190+
schema_config = ModelSchemaConfig(
191+
read_only_fields=["id", "created_at", "updated_at"],
192+
depth=1
193+
)
194+
195+
@api_controller("/events")
196+
class EventModelController(ModelControllerBase):
197+
model_config = BaseModelConfig(
198+
model=Event,
199+
# Override or extend base configuration
200+
allowed_routes=["list", "find_one"]
201+
)
202+
```
203+
204+
## **Best Practices**
205+
206+
1. **Schema Configuration**:
207+
- Always specify `read_only_fields` for auto-generated fields
208+
- Use `depth` carefully as it can impact performance
209+
- Consider using `exclude` for sensitive fields
210+
211+
2. **Route Configuration**:
212+
- Limit `allowed_routes` to only necessary endpoints
213+
- Provide meaningful summaries and descriptions
214+
- Use tags for API organization
215+
216+
3. **Pagination**:
217+
- Always set reasonable limits
218+
- Consider your data size when choosing pagination class
219+
- Use appropriate page sizes for your use case
220+
221+
4. **Async Support**:
222+
- Enable `async_routes` when using async database operations
223+
- Implement custom async services for complex operations
224+
- Consider performance implications of async operations

0 commit comments

Comments
 (0)