Models
Essential model field types, relationships, and database operations for Django ORM.
Basic Model Definition
Simple Model
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Categories"
ordering = ['name']
def __str__(self):
return self.name
Model Fields
String Fields
class MyModel(models.Model):
# Character field with max length
name = models.CharField(max_length=100)
# Text field for large content
description = models.TextField()
# Slug field for URL-friendly strings
slug = models.SlugField(max_length=50, unique=True)
# Email field with validation
email = models.EmailField()
# URL field with validation
website = models.URLField()
# JSON field (PostgreSQL, MySQL 5.7+)
metadata = models.JSONField(default=dict)
Numeric Fields
class Product(models.Model):
# Integer field
quantity = models.IntegerField(default=0)
# Positive integer
stock = models.PositiveIntegerField()
# Decimal field for precise numbers
price = models.DecimalField(max_digits=10, decimal_places=2)
# Float field
weight = models.FloatField()
Date and Time Fields
class Event(models.Model):
# Date field
event_date = models.DateField()
# Time field
event_time = models.TimeField()
# DateTime field
event_datetime = models.DateTimeField()
# Auto-created timestamp
created_at = models.DateTimeField(auto_now_add=True)
# Auto-updated timestamp
updated_at = models.DateTimeField(auto_now=True)
Choice Fields
class Profile(models.Model):
GENDER_CHOICES = [
('M', 'Male'),
('F', 'Female'),
('O', 'Other'),
]
STATUS_CHOICES = [
('active', 'Active'),
('inactive', 'Inactive'),
('pending', 'Pending'),
]
# Boolean field
is_active = models.BooleanField(default=True)
# Choice field
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending')
File Fields
class Document(models.Model):
# File field
document = models.FileField(upload_to='documents/')
# Image field
image = models.ImageField(upload_to='images/', blank=True)
# Custom upload path
def user_directory_path(instance, filename):
return f'user_{instance.user.id}/{filename}'
avatar = models.ImageField(upload_to=user_directory_path)
Field Options
Common Options
class MyModel(models.Model):
# Required field
name = models.CharField(max_length=100)
# Optional field (can be empty in forms)
description = models.TextField(blank=True)
# Nullable field (can be NULL in database)
notes = models.TextField(null=True)
# Both blank and null
optional_field = models.CharField(max_length=100, blank=True, null=True)
# Default value
status = models.CharField(max_length=20, default='active')
# Unique constraint
email = models.EmailField(unique=True)
# Database index
code = models.CharField(max_length=50, db_index=True)
# Verbose name for admin
full_name = models.CharField(max_length=100, verbose_name="Full Name")
# Help text for forms
bio = models.TextField(help_text="Brief description about yourself")
Relationships
One-to-Many (ForeignKey)
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
# Custom related name
category = models.ForeignKey(
'Category',
on_delete=models.SET_NULL,
null=True,
related_name='books'
)
Many-to-Many
class Tag(models.Model):
name = models.CharField(max_length=50)
class Article(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag, blank=True)
# Many-to-many with through model
authors = models.ManyToManyField(
'Author',
through='ArticleAuthor',
related_name='articles'
)
class ArticleAuthor(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
role = models.CharField(max_length=50)
One-to-One
class User(models.Model):
username = models.CharField(max_length=100)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
avatar = models.ImageField(upload_to='avatars/')
Model Meta Options
Common Meta Options
class Article(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# Custom table name
db_table = 'blog_articles'
# Ordering
ordering = ['-created_at', 'title']
# Verbose names
verbose_name = 'Article'
verbose_name_plural = 'Articles'
# Unique constraints
unique_together = ['title', 'author']
# Database indexes
indexes = [
models.Index(fields=['title']),
models.Index(fields=['created_at', 'title']),
]
# Permissions
permissions = [
('can_publish', 'Can publish article'),
]
Custom Model Methods
String Representation
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
def __str__(self):
return f"{self.title} by {self.author}"
Custom Methods
class Order(models.Model):
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
discount = models.DecimalField(max_digits=5, decimal_places=2, default=0)
def get_subtotal(self):
return self.total_amount - self.discount
def get_final_amount(self):
return self.get_subtotal() * 1.08 # With tax
URL Methods
class Article(models.Model):
slug = models.SlugField(unique=True)
def get_absolute_url(self):
return reverse('article_detail', kwargs={'slug': self.slug})
Custom Managers
Custom Manager
class PublishedManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(published=True)
class Article(models.Model):
title = models.CharField(max_length=200)
published = models.BooleanField(default=False)
objects = models.Manager() # Default manager
published_objects = PublishedManager() # Custom manager
# Usage
published_articles = Article.published_objects.all()
Migrations
Migration Commands
# Create migration for changes
python manage.py makemigrations
# Create migration for specific app
python manage.py makemigrations myapp
# Create empty migration
python manage.py makemigrations --empty myapp
# Apply migrations
python manage.py migrate
# Show migration status
python manage.py showmigrations
# Show SQL for migration
python manage.py sqlmigrate myapp 0001
Data Migration
# migrations/0002_populate_data.py
from django.db import migrations
def populate_categories(apps, schema_editor):
Category = apps.get_model('myapp', 'Category')
categories = ['Technology', 'Sports', 'Entertainment']
for name in categories:
Category.objects.get_or_create(name=name)
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RunPython(populate_categories),
]
Abstract Models
Abstract Base Classes
class TimeStampedModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Article(TimeStampedModel):
title = models.CharField(max_length=200)
content = models.TextField()