Examples

This page contains practical examples of using Django Cruder in real-world scenarios.

Basic Contact Management

A simple contact management system:

# models.py
from django.db import models

class Contact(models.Model):
    name = models.CharField(max_length=100, verbose_name="Full Name")
    email = models.EmailField(verbose_name="Email Address")
    phone = models.CharField(max_length=20, verbose_name="Phone Number")
    company = models.CharField(max_length=100, blank=True)
    active = models.BooleanField(default=True)
    notes = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

# views.py
from django.contrib.auth.decorators import login_required
from cruder import crud_view
from .models import Contact

@login_required
def contact_crud(request, pk=None, action='list'):
    return crud_view(
        Contact,
        exclude_fields=['created_at'],
        list_fields=['name', 'email', 'phone', 'company', 'active'],
        search_fields=['name', 'email', 'phone', 'company'],
        per_page=20
    )(request, pk, action)

# urls.py
from cruder.urls import crud_urlpatterns
urlpatterns = crud_urlpatterns('contacts', views.contact_crud)

E-commerce Product Catalog

A product catalog with different permission levels:

# models.py
class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=200)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField(default=0)
    description = models.TextField()
    active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

# views.py
from django.contrib.auth.decorators import login_required
from cruder import crud_view

@login_required
def product_crud(request, pk=None, action='list'):
    return crud_view(
        Product,
        exclude_fields=['created_at'],
        list_fields=['name', 'category', 'price', 'stock', 'active'],
        search_fields=['name', 'description'],
        permissions={
            'C': ['admin', 'manager'],  # Only admin/manager can create
            'U': ['admin', 'manager'],  # Only admin/manager can update
            'D': ['admin']              # Only admin can delete
        },
        per_page=25
    )(request, pk, action)

@login_required
def category_crud(request, pk=None, action='list'):
    return crud_view(
        Category,
        permissions={'C': ['admin'], 'U': ['admin'], 'D': ['admin']},
        search_fields=['name']
    )(request, pk, action)

Project Management System

A project tracking system with read-only fields:

# models.py
class Project(models.Model):
    STATUS_CHOICES = [
        ('planning', 'Planning'),
        ('active', 'Active'),
        ('on_hold', 'On Hold'),
        ('completed', 'Completed'),
    ]

    name = models.CharField(max_length=200)
    description = models.TextField()
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='planning')
    start_date = models.DateField()
    end_date = models.DateField(null=True, blank=True)
    budget = models.DecimalField(max_digits=10, decimal_places=2)
    manager = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

# views.py
@login_required
def project_crud(request, pk=None, action='list'):
    return crud_view(
        Project,
        exclude_fields=['created_at', 'updated_at'],
        list_fields=['name', 'status', 'start_date', 'manager', 'budget'],
        readonly_fields=['created_at', 'manager'],  # Manager set on creation
        search_fields=['name', 'description'],
        per_page=15
    )(request, pk, action)

Multi-Framework Example

Using different frameworks for different sections:

# Admin interface with Bootstrap
@user_passes_test(lambda u: u.is_staff)
def admin_users_crud(request, pk=None, action='list'):
    return crud_view(
        User,
        framework='bootstrap',
        list_fields=['username', 'email', 'first_name', 'last_name', 'is_active'],
        readonly_fields=['username', 'date_joined'],
        permissions={'D': ['admin']},
        search_fields=['username', 'email', 'first_name', 'last_name']
    )(request, pk, action)

# Public-facing interface with Bulma
@login_required
def public_profile_crud(request, pk=None, action='list'):
    return crud_view(
        UserProfile,
        framework='bulma',
        exclude_fields=['user'],
        readonly_mode=True  # Users can only view, not edit
    )(request, pk, action)

Read-Only Dashboard

A dashboard view that only allows viewing data:

# models.py
class SalesReport(models.Model):
    date = models.DateField()
    total_sales = models.DecimalField(max_digits=10, decimal_places=2)
    orders_count = models.IntegerField()
    top_product = models.CharField(max_length=200)
    region = models.CharField(max_length=100)

    class Meta:
        ordering = ['-date']

# views.py
@login_required
def sales_dashboard(request, pk=None, action='list'):
    return crud_view(
        SalesReport,
        readonly_mode=True,  # No create/edit/delete
        list_fields=['date', 'total_sales', 'orders_count', 'top_product', 'region'],
        search_fields=['region', 'top_product'],
        per_page=50
    )(request, pk, action)

Custom URL Structure

Using custom URL patterns instead of the helper:

# urls.py
from django.urls import path
from . import views

app_name = 'inventory'

urlpatterns = [
    # Custom URL structure
    path('items/', views.item_crud, name='item_list'),
    path('items/add/', views.item_crud, {'action': 'create'}, name='item_add'),
    path('items/<int:pk>/view/', views.item_crud, {'action': 'view'}, name='item_view'),
    path('items/<int:pk>/modify/', views.item_crud, {'action': 'edit'}, name='item_modify'),
    path('items/<int:pk>/remove/', views.item_crud, {'action': 'delete'}, name='item_remove'),

    # Categories with standard URLs
] + crud_urlpatterns('categories', views.category_crud)

Blog Management

A blog with different access levels:

# models.py
class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

# views.py
# Authors can edit their own posts
@login_required
def my_posts_crud(request, pk=None, action='list'):
    return crud_view(
        BlogPost,
        queryset=BlogPost.objects.filter(author=request.user),
        exclude_fields=['author', 'created_at', 'updated_at'],
        readonly_fields=['slug'],
        search_fields=['title', 'content']
    )(request, pk, action)

# Editors can manage all posts
@user_passes_test(lambda u: u.groups.filter(name='editors').exists())
def all_posts_crud(request, pk=None, action='list'):
    return crud_view(
        BlogPost,
        list_fields=['title', 'author', 'published', 'created_at'],
        search_fields=['title', 'content', 'author__username'],
        per_page=30
    )(request, pk, action)