Django Model Forms

Creating forms directly from database models

🗄️ What are Model Forms?

Model Forms automatically create forms from your database models. They save time by generating form fields from model fields and handle saving data to the database automatically.


# forms.py - Create form from model
from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'content', 'author']
                                    

Model Form Benefits

Auto-Generation

Fields created from model

No duplication Less code
💾

Easy Saving

Direct database save

form.save() Automatic

Built-in Validation

Model constraints applied

Type checking Required fields
🔄

Update Support

Edit existing records

Instance editing Simple updates

🔹 Creating a Model

First, define your database model. Models represent database tables with fields defining the data structure and types for each column.

# models.py
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.CharField(max_length=100)
    isbn = models.CharField(max_length=13, unique=True)
    published_date = models.DateField()
    pages = models.IntegerField()
    description = models.TextField()
    
    def __str__(self):
        return self.title

🔹 Creating a Model Form

ModelForm automatically generates form fields from your model. Specify which fields to include or exclude using the Meta class configuration options.

# forms.py
from django import forms
from .models import Book

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = ['title', 'author', 'isbn', 
                  'published_date', 'pages', 'description']
        
        # OR exclude specific fields
        # exclude = ['id']
        
        # OR include all fields
        # fields = '__all__'
        
        # Custom labels
        labels = {
            'isbn': 'ISBN Number',
            'published_date': 'Publication Date'
        }
        
        # Custom widgets
        widgets = {
            'description': forms.Textarea(attrs={'rows': 4}),
            'published_date': forms.DateInput(attrs={'type': 'date'})
        }

🔹 Using Model Forms in Views

Model forms simplify view logic with built-in save functionality. Handle both creating new records and updating existing ones with minimal code.

# views.py
from django.shortcuts import render, redirect
from .forms import BookForm

def add_book(request):
    if request.method == 'POST':
        form = BookForm(request.POST)
        if form.is_valid():
            form.save()  # Automatically saves to database
            return redirect('book_list')
    else:
        form = BookForm()
    
    return render(request, 'add_book.html', {'form': form})

# Edit existing book
def edit_book(request, book_id):
    book = Book.objects.get(id=book_id)
    
    if request.method == 'POST':
        form = BookForm(request.POST, instance=book)
        if form.is_valid():
            form.save()
            return redirect('book_detail', book_id=book.id)
    else:
        form = BookForm(instance=book)
    
    return render(request, 'edit_book.html', {'form': form})

🔹 Customizing Model Forms

Override form methods to add custom validation or modify data before saving. Use clean methods for field-specific or form-wide validation logic.

# forms.py
from django import forms
from .models import Book

class BookForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = '__all__'
    
    # Custom field validation
    def clean_pages(self):
        pages = self.cleaned_data.get('pages')
        if pages < 1:
            raise forms.ValidationError('Pages must be positive')
        return pages
    
    # Custom form validation
    def clean(self):
        cleaned_data = super().clean()
        title = cleaned_data.get('title')
        author = cleaned_data.get('author')
        
        # Check for duplicate
        if Book.objects.filter(title=title, author=author).exists():
            raise forms.ValidationError('This book already exists')
        
        return cleaned_data
    
    # Modify before saving
    def save(self, commit=True):
        book = super().save(commit=False)
        book.title = book.title.title()  # Capitalize
        if commit:
            book.save()
        return book

🔹 Partial Updates

Update only specific fields without requiring all form data. Useful for forms that modify a subset of model fields.

# forms.py - Form with limited fields
class BookUpdateForm(forms.ModelForm):
    class Meta:
        model = Book
        fields = ['title', 'description']  # Only these fields

# views.py
def quick_edit(request, book_id):
    book = Book.objects.get(id=book_id)
    form = BookUpdateForm(request.POST or None, instance=book)
    
    if form.is_valid():
        form.save()  # Only updates title and description
        return redirect('book_detail', book_id=book.id)
    
    return render(request, 'quick_edit.html', {'form': form})

🧠 Test Your Knowledge

What does form.save() do in a ModelForm?