Managers

Managers provide the primary interface for database-table operations in django-api-orm. Every model has a objects manager that provides methods for creating, retrieving, and managing instances.

Accessing Managers

The default manager is available as objects on your model:

# Access the manager
User.objects

# Manager is only available after model registration
from django_api_orm import ServiceClient, register_models

with ServiceClient(base_url="https://api.example.com") as client:
    register_models(client, User)
    # Now User.objects is available

Manager Methods

QuerySet Methods

All QuerySet methods are available on the manager:

# These are equivalent
User.objects.filter(active=True)
User.objects.all().filter(active=True)

# Manager methods return QuerySets
queryset = User.objects.filter(active=True)
admin_queryset = queryset.filter(role="admin")

See QuerySets for complete QuerySet documentation.

Creation Methods

create()

Create and save a new instance:

user = User.objects.create(
    name="Alice Smith",
    email="alice@example.com",
    active=True
)

# Equivalent to:
user = User(name="Alice Smith", email="alice@example.com", active=True, _client=client)
user.save()

This method:

  1. Validates the data using Pydantic

  2. Makes a POST request to the API

  3. Returns the created instance with API-assigned fields (like id)

bulk_create()

Create multiple instances at once:

users = User.objects.bulk_create([
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "Bob", "email": "bob@example.com"},
    {"name": "Charlie", "email": "charlie@example.com"}
])

# Returns a list of created instances
for user in users:
    print(f"Created user {user.id}: {user.name}")

Note: This makes individual POST requests for each instance (no bulk endpoint is assumed).

get_or_create()

Get an existing instance or create a new one:

user, created = User.objects.get_or_create(
    email="alice@example.com",
    defaults={"name": "Alice Smith", "role": "user"}
)

if created:
    print(f"Created new user: {user.name}")
else:
    print(f"Found existing user: {user.name}")

The first argument(s) are used for the lookup, and defaults provides additional fields for creation.

update_or_create()

Update an existing instance or create a new one:

user, created = User.objects.update_or_create(
    email="alice@example.com",
    defaults={"name": "Alice Updated", "role": "admin"}
)

if created:
    print(f"Created new user: {user.name}")
else:
    print(f"Updated existing user: {user.name}")

This method:

  1. Tries to get an instance matching the lookup fields

  2. If found, updates it with the defaults fields

  3. If not found, creates a new instance with all fields

Retrieval Methods

all()

Get all instances:

all_users = User.objects.all()

Returns a QuerySet that can be further filtered:

active_users = User.objects.all().filter(active=True)

filter()

Filter instances by field values:

active_users = User.objects.filter(active=True)
admins = User.objects.filter(role="admin")

# Multiple filters (AND logic)
active_admins = User.objects.filter(active=True, role="admin")

exclude()

Exclude instances by field values:

non_admins = User.objects.exclude(role="admin")
active_non_guests = User.objects.filter(active=True).exclude(role="guest")

get()

Get a single instance:

from django_api_orm.exceptions import DoesNotExist, MultipleObjectsReturned

try:
    user = User.objects.get(id=1)
except DoesNotExist:
    print("User not found")
except MultipleObjectsReturned:
    print("Multiple users found")

first()

Get the first instance or None:

first_user = User.objects.first()
if first_user:
    print(f"First user: {first_user.name}")

# With ordering
newest_user = User.objects.order_by("-created_at").first()

last()

Get the last instance or None:

last_user = User.objects.last()
if last_user:
    print(f"Last user: {last_user.name}")

# With ordering
oldest_user = User.objects.order_by("created_at").last()

Utility Methods

count()

Get the count of instances:

total_users = User.objects.count()
active_users = User.objects.filter(active=True).count()

print(f"{active_users} out of {total_users} users are active")

exists()

Check if any instances exist:

has_users = User.objects.exists()
has_admins = User.objects.filter(role="admin").exists()

if not has_admins:
    print("No admin users found!")

Ordering Methods

order_by()

Order instances by one or more fields:

# Ascending order
users = User.objects.order_by("name")

# Descending order
users = User.objects.order_by("-created_at")

# Multiple fields
users = User.objects.order_by("role", "-name")

Async Managers

Async managers work the same way but require await for operations that return instances or data:

from django_api_orm import AsyncAPIModel, AsyncServiceClient, register_async_models

class User(AsyncAPIModel):
    _schema_class = UserSchema
    _endpoint = "/api/v1/users/"

async with AsyncServiceClient(base_url="https://api.example.com") as client:
    register_async_models(client, User)

    # Await creation
    user = await User.objects.create(name="Alice", email="alice@example.com")

    # Await retrieval
    user = await User.objects.get(id=1)
    first_user = await User.objects.first()

    # Await get_or_create
    user, created = await User.objects.get_or_create(
        email="alice@example.com",
        defaults={"name": "Alice"}
    )

    # QuerySet methods don't need await until evaluation
    queryset = User.objects.filter(active=True)  # No await
    async for user in queryset:  # Await during iteration
        print(user.name)

    # Await count/exists
    count = await User.objects.count()
    exists = await User.objects.exists()

See Async Support for complete async documentation.

Custom Managers

You can create custom managers to encapsulate common queries:

from django_api_orm import Manager, APIModel

class UserManager(Manager):
    """Custom manager for User model."""

    def active(self):
        """Get only active users."""
        return self.filter(active=True)

    def admins(self):
        """Get only admin users."""
        return self.filter(role="admin")

    def active_admins(self):
        """Get active admin users."""
        return self.active().admins()

class User(APIModel):
    _schema_class = UserSchema
    _endpoint = "/api/v1/users/"

    # Override the default manager
    objects = UserManager()

# Usage
active_users = User.objects.active()
admins = User.objects.admins()
active_admins = User.objects.active_admins()

Best Practices

  1. Use create() over manual instantiation: More concise and handles client injection

  2. Use get_or_create() for idempotency: Prevents duplicate creation

  3. Use exists() instead of count() > 0: More efficient for existence checks

  4. Chain filters for readability: User.objects.filter(active=True).filter(role="admin")

  5. Use custom managers for common queries: Encapsulate business logic

  6. Remember async/await: Don’t forget to await async manager methods

Common Patterns

Pagination

page_size = 20
page = 1

# Get page 1 (offset 0, limit 20)
users = User.objects.all()[(page - 1) * page_size:page * page_size]

Conditional Creation

# Only create if doesn't exist
if not User.objects.filter(email="alice@example.com").exists():
    User.objects.create(name="Alice", email="alice@example.com")

# Or use get_or_create
user, created = User.objects.get_or_create(
    email="alice@example.com",
    defaults={"name": "Alice"}
)

Batch Processing

# Create multiple users
users_data = [
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "Bob", "email": "bob@example.com"},
    {"name": "Charlie", "email": "charlie@example.com"}
]
users = User.objects.bulk_create(users_data)

# Process in batches
batch_size = 100
offset = 0
while True:
    batch = User.objects.all()[offset:offset + batch_size]
    if not batch:
        break
    for user in batch:
        # Process user
        pass
    offset += batch_size

Finding or Creating Defaults

# Find admin or create a default one
admin, created = User.objects.get_or_create(
    role="admin",
    defaults={
        "name": "Default Admin",
        "email": "admin@example.com",
        "active": True
    }
)

Examples

# Create a user
user = User.objects.create(
    name="Alice Smith",
    email="alice@example.com",
    role="admin"
)

# Get or create pattern
user, created = User.objects.get_or_create(
    email="bob@example.com",
    defaults={"name": "Bob Jones", "role": "user"}
)

# Update or create pattern
user, created = User.objects.update_or_create(
    email="charlie@example.com",
    defaults={"name": "Charlie Brown", "role": "user", "active": True}
)

# Bulk creation
users = User.objects.bulk_create([
    {"name": f"User {i}", "email": f"user{i}@example.com"}
    for i in range(10)
])

# Common queries via manager
active_count = User.objects.filter(active=True).count()
has_admins = User.objects.filter(role="admin").exists()
newest_user = User.objects.order_by("-created_at").first()

Next Steps