Skip to main content

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"})

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_process hook for side effects (runs after commit)
  • Type-safe with generics support
  • ModelField and MultipleModelField for 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 logic
  • post_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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django_ninja_service_objects-0.1.2.tar.gz (7.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_ninja_service_objects-0.1.2-py3-none-any.whl (6.7 kB view details)

Uploaded Python 3

File details

Details for the file django_ninja_service_objects-0.1.2.tar.gz.

File metadata

File hashes

Hashes for django_ninja_service_objects-0.1.2.tar.gz
Algorithm Hash digest
SHA256 9f562cc667f1799edcb66d284e869431fa7d6bbad01d186479accdf6e9c4bf09
MD5 d7c03a8d7f20d111c982f36eec003855
BLAKE2b-256 755068e28bf90bd1eed7689dacc8f704bdc0b605acc2af328440d7bece114528

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_ninja_service_objects-0.1.2.tar.gz:

Publisher: publish.yml on bekindsoft/ninja-service-objects

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file django_ninja_service_objects-0.1.2-py3-none-any.whl.

File metadata

File hashes

Hashes for django_ninja_service_objects-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 80956e7100a7ee1fa45c0ec0693acafa40ac5db281e716286c30910d880befe1
MD5 e515b37c461ec3568695d3d2bb81e4fe
BLAKE2b-256 8142552596abf5a6aabe1463d13521aa2b1ce42694cfb5f5f3158ed565a49dc9

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_ninja_service_objects-0.1.2-py3-none-any.whl:

Publisher: publish.yml on bekindsoft/ninja-service-objects

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page