Service objects for Django Ninja using Pydantic validation
Project description
Django Ninja Service Objects
An implementation of the django-service-objects philosophy for Django Ninja with Pydantic validation.
Encapsulate your business logic in reusable, testable service classes.
Installation
pip install django-ninja-service-objects
Add to your Django settings:
# settings.py
INSTALLED_APPS = [
...
'ninja_service_objects',
...
]
Usage
from ninja import Schema
from ninja_service_objects import Service
class CreateUserInput(Schema):
email: str
name: str
class CreateUserService(Service[CreateUserInput, User]):
schema = CreateUserInput
def process(self) -> User:
return User.objects.create(
email=self.cleaned_data.email,
name=self.cleaned_data.name,
)
def post_process(self) -> None:
# Called after successful transaction commit
send_welcome_email(self.cleaned_data.email)
# In your view
user = CreateUserService.execute({"email": "test@example.com", "name": "Test"})
Decorator-Based Services
For smaller operations, use service_object to get the same validation and
transaction handling without defining a service class:
from ninja import Schema
from ninja_service_objects import service_object
class CreateUserInput(Schema):
email: str
name: str
def send_welcome_email_after_commit(user: User) -> None:
send_welcome_email(user.email)
@service_object(post_process=send_welcome_email_after_commit)
def create_user(data: CreateUserInput) -> User:
return User.objects.create(
email=data.email,
name=data.name,
)
user = create_user({"email": "test@example.com", "name": "Test"})
The decorator validates any function argument annotated with a Pydantic model
or Ninja schema. Use db_transaction=False to disable the transaction wrapper,
using="other_db" to select a database alias, or post_process=callback to run
a side effect after a successful commit. The callback receives the service
function result.
Using Pydantic BaseModel with Custom Validators
You can also use Pydantic's BaseModel directly for more complex validation:
from pydantic import BaseModel, EmailStr, field_validator, model_validator
from ninja_service_objects import Service
class RegisterUserInput(BaseModel):
email: EmailStr
password: str
password_confirm: str
@field_validator("password")
@classmethod
def password_min_length(cls, v: str) -> str:
if len(v) < 8:
raise ValueError("Password must be at least 8 characters")
return v
@model_validator(mode="after")
def passwords_match(self) -> "RegisterUserInput":
if self.password != self.password_confirm:
raise ValueError("Passwords do not match")
return self
class RegisterUserService(Service[RegisterUserInput, User]):
schema = RegisterUserInput
def process(self) -> User:
return User.objects.create_user(
email=self.cleaned_data.email,
password=self.cleaned_data.password,
)
Using ModelField for Django Model Instances
Use ModelField and MultipleModelField to validate Django model instances as service inputs:
from pydantic import BaseModel
from ninja_service_objects import Service, ModelField, MultipleModelField
class TransferOwnershipInput(BaseModel):
from_user: ModelField[User]
to_user: ModelField[User]
posts: MultipleModelField[Post]
class TransferOwnershipService(Service[TransferOwnershipInput, None]):
schema = TransferOwnershipInput
def process(self) -> None:
for post in self.cleaned_data.posts:
post.author = self.cleaned_data.to_user
post.save()
By default, ModelField rejects unsaved model instances (objects without a primary key). To allow unsaved instances:
from typing import Annotated
class MyInput(BaseModel):
user: Annotated[User, ModelField(allow_unsaved=True)]
items: Annotated[list[Item], MultipleModelField(allow_unsaved=True)]
Features
- Pydantic validation for inputs
- Automatic database transaction handling
post_processhook for side effects (runs after commit)- Decorator-based services for lightweight operations
- Type-safe with generics support
ModelFieldandMultipleModelFieldfor Django model instance validation
Design Decisions
Why Pydantic instead of Django Forms?
The original django-service-objects uses Django Forms for validation. Since Django Ninja already uses Pydantic for request/response schemas, this library uses Pydantic to:
- Avoid mixing two validation systems in the same project
- Reuse your existing Django Ninja schemas as service inputs
- Get better type hints and IDE support
API Compatibility
We maintain familiar patterns from django-service-objects:
cleaned_data- Access validated input data (same naming as Django forms/original library)process()- Override this with your business logicpost_process()- Runs after successful transaction commit (for emails, notifications, etc.)execute()- Class method entry point that handles validation and transactions
What's Different
| django-service-objects | ninja-service-objects |
|---|---|
| Django Forms validation | Pydantic validation |
service_clean() method |
Pydantic validators |
| Form fields | Pydantic BaseModel |
is_valid() + execute() |
Single execute() call |
Configuration
Transaction Control
class MyService(Service[MyInput, MyOutput]):
schema = MyInput
db_transaction = False # Disable automatic transaction wrapping
using = "other_db" # Use a different database alias
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_ninja_service_objects-0.1.3.tar.gz.
File metadata
- Download URL: django_ninja_service_objects-0.1.3.tar.gz
- Upload date:
- Size: 9.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
182c0c9304f6c10f2d8f3b6845576be7a2e0779ab9280c52a17fed4e26a7cfab
|
|
| MD5 |
f2e331dab48e5a30e498ca7865b1e417
|
|
| BLAKE2b-256 |
669409385a87ad0388febd4bef533350d23773cda94b39e65d3b6d0d3cdb4d0e
|
Provenance
The following attestation bundles were made for django_ninja_service_objects-0.1.3.tar.gz:
Publisher:
publish.yml on bekindsoft/ninja-service-objects
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_ninja_service_objects-0.1.3.tar.gz -
Subject digest:
182c0c9304f6c10f2d8f3b6845576be7a2e0779ab9280c52a17fed4e26a7cfab - Sigstore transparency entry: 1437856257
- Sigstore integration time:
-
Permalink:
bekindsoft/ninja-service-objects@1b8a5b3b2c806491e4e2d2752a6d03f3824cb2e2 -
Branch / Tag:
refs/tags/django-ninja-service-objects-v0.1.3 - Owner: https://github.com/bekindsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1b8a5b3b2c806491e4e2d2752a6d03f3824cb2e2 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file django_ninja_service_objects-0.1.3-py3-none-any.whl.
File metadata
- Download URL: django_ninja_service_objects-0.1.3-py3-none-any.whl
- Upload date:
- Size: 8.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f027cc9c0bb48965df69da9d48f62888f42a17863519bd4f52bc027ff4a45287
|
|
| MD5 |
5c51c31d4afde1d37c1d9d25c72d7296
|
|
| BLAKE2b-256 |
dce58b6241313131967a4fd8c39bfcad1e58bfc377be589c3f9a106e6130ca0d
|
Provenance
The following attestation bundles were made for django_ninja_service_objects-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on bekindsoft/ninja-service-objects
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_ninja_service_objects-0.1.3-py3-none-any.whl -
Subject digest:
f027cc9c0bb48965df69da9d48f62888f42a17863519bd4f52bc027ff4a45287 - Sigstore transparency entry: 1437856290
- Sigstore integration time:
-
Permalink:
bekindsoft/ninja-service-objects@1b8a5b3b2c806491e4e2d2752a6d03f3824cb2e2 -
Branch / Tag:
refs/tags/django-ninja-service-objects-v0.1.3 - Owner: https://github.com/bekindsoft
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1b8a5b3b2c806491e4e2d2752a6d03f3824cb2e2 -
Trigger Event:
workflow_dispatch
-
Statement type: