Skip to main content

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()