$ cat /posts/django-crud-operations-create-read-update-delete-with-models.md

Django CRUD Operations: Create, Read, Update, Delete with Models

drwxr-xr-x2026-01-225 min0 views
Django CRUD Operations: Create, Read, Update, Delete with Models

Django CRUD operations form the foundation of database interactions enabling applications to Create, Read, Update, and Delete records using Django's powerful Object-Relational Mapping (ORM) system. Understanding CRUD operations is essential for building dynamic web applications that manage data effectively from user registration and blog posts to e-commerce products and customer records. Django ORM abstracts SQL queries into Python code making database operations intuitive, secure, and database-agnostic supporting PostgreSQL, MySQL, SQLite, and other databases. Mastering CRUD operations enables building complete data-driven applications with proper data management, validation, and business logic implementation supporting everything from simple contact forms to complex enterprise resource planning systems.

Create Operations

Creating records in Django involves instantiating model objects and calling save() method or using create() for single-line creation. Django provides multiple approaches including bulk creation for performance, get_or_create() to avoid duplicates, and update_or_create() for upsert operations. Understanding creation patterns enables efficient data insertion with proper validation and error handling.

pythoncreate_operations.py
# Django Create Operations
from django.shortcuts import render, redirect
from .models import Post, Author, Category
from django.utils import timezone

# Method 1: Create and save
def create_post_view(request):
    if request.method == 'POST':
        # Create new instance
        post = Post()
        post.title = request.POST['title']
        post.content = request.POST['content']
        post.author = request.user
        post.published_date = timezone.now()
        post.save()  # Save to database
        
        return redirect('post_detail', pk=post.pk)

# Method 2: Create in one line
def create_post_shorthand(request):
    post = Post.objects.create(
        title='My New Post',
        content='Post content here',
        author=request.user,
        published_date=timezone.now()
    )
    # Already saved, no need to call save()
    return redirect('post_detail', pk=post.pk)

# Method 3: Bulk create (efficient for multiple records)
def bulk_create_posts():
    posts = [
        Post(title='Post 1', content='Content 1', author_id=1),
        Post(title='Post 2', content='Content 2', author_id=1),
        Post(title='Post 3', content='Content 3', author_id=1),
    ]
    Post.objects.bulk_create(posts)
    # Creates all records in single database query

# Method 4: Get or create (avoid duplicates)
def get_or_create_category(name):
    category, created = Category.objects.get_or_create(
        name=name,
        defaults={'description': 'Default description'}
    )
    if created:
        print(f"Created new category: {name}")
    else:
        print(f"Category already exists: {name}")
    return category

# Method 5: Update or create (upsert)
def update_or_create_author(username):
    author, created = Author.objects.update_or_create(
        username=username,
        defaults={
            'email': f'{username}@example.com',
            'bio': 'Default bio'
        }
    )
    return author

# With validation
def create_with_validation(request):
    try:
        post = Post(
            title=request.POST['title'],
            content=request.POST['content'],
            author=request.user
        )
        post.full_clean()  # Validate before saving
        post.save()
        return redirect('success')
    except ValidationError as e:
        return render(request, 'form.html', {'errors': e.message_dict})

# Create with related objects
def create_post_with_tags(request):
    post = Post.objects.create(
        title='Django Tutorial',
        content='Learn Django',
        author=request.user
    )
    # Add many-to-many relationships
    post.tags.add(tag1, tag2, tag3)
    # Or create and add
    post.tags.create(name='Python')
    
    return post

Read Operations

Reading data from Django models uses QuerySet API providing methods like all(), filter(), get(), and exclude() for retrieving records. Understanding read operations enables querying databases efficiently with filtering, ordering, and relationship traversal. Django QuerySets are lazy meaning queries execute only when data is evaluated optimizing performance.

pythonread_operations.py
# Django Read Operations
from .models import Post, Author, Comment
from django.shortcuts import render, get_object_or_404

# Get all records
def list_all_posts(request):
    posts = Post.objects.all()
    return render(request, 'posts/list.html', {'posts': posts})

# Get single record
def get_post_detail(request, pk):
    # Method 1: get() - raises exception if not found
    try:
        post = Post.objects.get(pk=pk)
    except Post.DoesNotExist:
        return render(request, '404.html')
    
    # Method 2: get_object_or_404 (recommended)
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'posts/detail.html', {'post': post})

# Filter records
def filter_posts(request):
    # Single condition
    published_posts = Post.objects.filter(status='published')
    
    # Multiple conditions (AND)
    recent_posts = Post.objects.filter(
        status='published',
        published_date__gte=timezone.now() - timedelta(days=7)
    )
    
    # Exclude records
    active_posts = Post.objects.exclude(status='draft')
    
    # Chaining filters
    posts = Post.objects.filter(status='published').exclude(title__icontains='test')
    
    return render(request, 'posts/list.html', {'posts': posts})

# Ordering
def ordered_posts(request):
    # Ascending order
    posts = Post.objects.order_by('published_date')
    
    # Descending order
    posts = Post.objects.order_by('-published_date')
    
    # Multiple fields
    posts = Post.objects.order_by('-published_date', 'title')
    
    return render(request, 'posts/list.html', {'posts': posts})

# Slicing (limit results)
def limited_posts(request):
    # First 5 posts
    posts = Post.objects.all()[:5]
    
    # Posts 10-20
    posts = Post.objects.all()[10:20]
    
    # First post
    first_post = Post.objects.first()
    
    # Last post
    last_post = Post.objects.last()
    
    return render(request, 'posts/list.html', {'posts': posts})

# Related objects
def posts_with_relations(request):
    # Forward relationship
    post = Post.objects.get(pk=1)
    author = post.author  # Access related author
    
    # Reverse relationship
    author = Author.objects.get(pk=1)
    author_posts = author.post_set.all()  # All posts by this author
    # Or with related_name:
    author_posts = author.posts.all()
    
    # Prefetch related (optimization)
    posts = Post.objects.select_related('author').prefetch_related('comments')
    
    return render(request, 'posts/list.html', {'posts': posts})

# Field lookups
def advanced_queries(request):
    # Exact match
    posts = Post.objects.filter(title__exact='Django Tutorial')
    
    # Case-insensitive
    posts = Post.objects.filter(title__iexact='django tutorial')
    
    # Contains
    posts = Post.objects.filter(title__contains='Django')
    
    # Starts with / Ends with
    posts = Post.objects.filter(title__startswith='Django')
    posts = Post.objects.filter(title__endswith='Tutorial')
    
    # Greater than / Less than
    posts = Post.objects.filter(views__gt=100)
    posts = Post.objects.filter(views__lte=50)
    
    # Date lookups
    posts = Post.objects.filter(published_date__year=2024)
    posts = Post.objects.filter(published_date__month=1)
    
    return posts

# Exists check
def check_exists(request):
    has_posts = Post.objects.filter(author=request.user).exists()
    
    # Count
    post_count = Post.objects.filter(status='published').count()
    
    return render(request, 'dashboard.html', {
        'has_posts': has_posts,
        'post_count': post_count
    })

Update Operations

Updating records in Django involves retrieving objects, modifying attributes, and saving changes or using update() method for bulk updates. Understanding update operations enables modifying existing data efficiently with proper validation and handling of concurrent modifications. Django provides both instance-level updates for single records and QuerySet updates for bulk operations.

pythonupdate_operations.py
# Django Update Operations
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from .models import Post
from .forms import PostForm

# Method 1: Update instance and save
def update_post_view(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    if request.method == 'POST':
        post.title = request.POST['title']
        post.content = request.POST['content']
        post.updated_at = timezone.now()
        post.save()  # Save changes
        
        return redirect('post_detail', pk=post.pk)
    
    return render(request, 'posts/edit.html', {'post': post})

# Method 2: Update specific fields only
def update_specific_fields(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.views += 1  # Increment views
    post.save(update_fields=['views'])  # Only update views field
    # More efficient than saving all fields

# Method 3: Bulk update (QuerySet update)
def bulk_update_posts():
    # Update multiple records at once
    Post.objects.filter(status='draft').update(
        status='published',
        published_date=timezone.now()
    )
    # Returns number of rows updated
    # Note: Does NOT call save() method or signals

# Update with form
def update_post_with_form(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            form.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm(instance=post)
    
    return render(request, 'posts/edit.html', {'form': form, 'post': post})

# Conditional update
def conditional_update(post_id, user):
    post = get_object_or_404(Post, pk=post_id)
    
    # Check permissions
    if post.author != user:
        raise PermissionDenied
    
    post.title = 'Updated Title'
    post.save()

# Update with F expressions (avoid race conditions)
from django.db.models import F

def increment_views(request, pk):
    # Thread-safe increment
    Post.objects.filter(pk=pk).update(views=F('views') + 1)
    
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'posts/detail.html', {'post': post})

# Update related objects
def update_with_relations(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    # Update many-to-many
    post.tags.clear()  # Remove all tags
    post.tags.add(tag1, tag2)  # Add new tags
    post.tags.set([tag1, tag2, tag3])  # Replace all tags
    
    # Update or create related
    comment, created = post.comments.update_or_create(
        user=request.user,
        defaults={'text': 'Updated comment'}
    )

# Partial update with validation
def update_with_validation(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    if request.method == 'POST':
        post.title = request.POST['title']
        
        try:
            post.full_clean()  # Validate
            post.save()
            return redirect('post_detail', pk=post.pk)
        except ValidationError as e:
            return render(request, 'posts/edit.html', {
                'post': post,
                'errors': e.message_dict
            })

# Atomic update (transaction)
from django.db import transaction

@transaction.atomic
def atomic_update(post_id):
    post = Post.objects.select_for_update().get(pk=post_id)
    post.status = 'published'
    post.published_date = timezone.now()
    post.save()
    # Changes rolled back if exception occurs

Delete Operations

Deleting records in Django uses delete() method on model instances or QuerySets supporting single and bulk deletions. Understanding delete operations includes handling cascading deletes through on_delete options, soft deletes for data retention, and proper authorization checks. Django automatically handles related object deletion based on ForeignKey on_delete settings ensuring referential integrity.

pythondelete_operations.py
# Django Delete Operations
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from .models import Post, Comment

# Method 1: Delete single instance
def delete_post_view(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    if request.method == 'POST':
        # Check permissions
        if post.author != request.user:
            messages.error(request, 'You cannot delete this post')
            return redirect('post_detail', pk=pk)
        
        post.delete()  # Delete from database
        messages.success(request, 'Post deleted successfully')
        return redirect('post_list')
    
    return render(request, 'posts/confirm_delete.html', {'post': post})

# Method 2: Bulk delete
def bulk_delete_posts():
    # Delete multiple records
    deleted_count, _ = Post.objects.filter(status='draft').delete()
    print(f"Deleted {deleted_count} draft posts")
    
    # Delete all (dangerous!)
    # Post.objects.all().delete()

# Delete with related objects
def delete_with_cascade():
    # When ForeignKey has on_delete=models.CASCADE
    author = Author.objects.get(pk=1)
    author.delete()  # Also deletes all related posts

# Soft delete (mark as deleted without removing)
class Post(models.Model):
    title = models.CharField(max_length=200)
    is_deleted = models.BooleanField(default=False)
    deleted_at = models.DateTimeField(null=True, blank=True)
    
    def soft_delete(self):
        self.is_deleted = True
        self.deleted_at = timezone.now()
        self.save()
    
    def restore(self):
        self.is_deleted = False
        self.deleted_at = None
        self.save()

def soft_delete_view(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.soft_delete()
    return redirect('post_list')

# Custom queryset for soft deletes
class PostQuerySet(models.QuerySet):
    def active(self):
        return self.filter(is_deleted=False)
    
    def deleted(self):
        return self.filter(is_deleted=True)

class PostManager(models.Manager):
    def get_queryset(self):
        return PostQuerySet(self.model, using=self._db)
    
    def active(self):
        return self.get_queryset().active()

# Usage
active_posts = Post.objects.active()

# Delete confirmation view
def delete_with_confirmation(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    if request.method == 'POST':
        if request.POST.get('confirm') == 'yes':
            post_title = post.title
            post.delete()
            messages.success(request, f'Post "{post_title}" deleted')
            return redirect('post_list')
    
    return render(request, 'posts/delete_confirm.html', {'post': post})

# Delete with backup
import json

def delete_with_backup(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    # Backup before deleting
    backup = {
        'title': post.title,
        'content': post.content,
        'author_id': post.author_id,
        'created_at': str(post.created_at)
    }
    
    # Save to file or archive table
    with open(f'backups/post_{pk}.json', 'w') as f:
        json.dump(backup, f)
    
    post.delete()
    return redirect('post_list')

# Atomic delete with related cleanup
from django.db import transaction

@transaction.atomic
def delete_with_cleanup(request, pk):
    post = get_object_or_404(Post, pk=pk)
    
    # Delete related objects first
    post.comments.all().delete()
    post.likes.all().delete()
    
    # Delete files if any
    if post.image:
        post.image.delete(save=False)
    
    # Finally delete post
    post.delete()
    
    return redirect('post_list')

CRUD Best Practices

Effective CRUD operations follow patterns ensuring data integrity, security, and performance. Always validate data before creating or updating using full_clean() or forms. Check user permissions before allowing updates or deletions preventing unauthorized access. Use transactions for operations affecting multiple records ensuring atomic operations. Implement soft deletes for important data allowing recovery. Use select_for_update() in transactions preventing race conditions. Optimize queries with select_related() and prefetch_related() reducing database hits. Handle exceptions properly providing meaningful error messages. Log important operations for audit trails. These practices ensure robust data management supporting reliable applications from simple blogs to complex enterprise systems.

$ cat /comments/ (0)

new_comment.sh

// Email hidden from public

>_

$ cat /comments/

// No comments found. Be the first!

[session] guest@{codershandbook}[timestamp] 2026

Navigation

Connect

Subscribe

// 2026 {Coders Handbook}. EOF.