Models and Databases
Learn how to define data models and work with databases in Django using the ORM (Object-Relational Mapping).
What are Models?
Models are Python classes that represent database tables. They define: - What data you store - How data is structured - Relationships between data
Django's ORM converts Python code to SQL automatically.
Creating Your First Model
Basic Model
# myapp/models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Model Explanation
models.Model- Base class for all Django modelsCharField- Text field with max lengthTextField- Large text field (unlimited length)DateTimeField- Date and time fieldauto_now_add=True- Set when createdauto_now=True- Update when saved
Common Field Types
Text Fields
# Short text
title = models.CharField(max_length=200)
# Long text
description = models.TextField()
# Email
email = models.EmailField()
# URL
website = models.URLField()
Number Fields
# Integer
age = models.IntegerField()
# Decimal
price = models.DecimalField(max_digits=10, decimal_places=2)
# Float
rating = models.FloatField()
# Boolean
is_published = models.BooleanField(default=False)
Date/Time Fields
# Date only
birth_date = models.DateField()
# Time only
start_time = models.TimeField()
# Date and time
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Choice Fields
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
('archived', 'Archived'),
]
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
Model Options
Field Options
class Post(models.Model):
title = models.CharField(
max_length=200,
null=False, # Can't be NULL in database
blank=False, # Required in forms
default='', # Default value
unique=True, # Must be unique
help_text='Post title' # Help text for forms
)
Model Meta Options
class Post(models.Model):
title = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at'] # Order by newest first
verbose_name = 'Blog Post' # Human-readable name
verbose_name_plural = 'Blog Posts'
Model Methods
str Method
Define how model is displayed:
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
Custom Methods
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def get_excerpt(self, length=100):
return self.content[:length] + '...' if len(self.content) > length else self.content
def is_recent(self):
from django.utils import timezone
return (timezone.now() - self.created_at).days < 7
Relationships
ForeignKey (Many-to-One)
One model references another:
class Author(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
class Post(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
ManyToManyField
Many-to-many relationship:
class Tag(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
title = models.CharField(max_length=200)
tags = models.ManyToManyField(Tag)
OneToOneField
One-to-one relationship:
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()
Migrations
Migrations are Django's way of applying model changes to the database.
Create Migrations
After creating or modifying models:
python manage.py makemigrations
This creates migration files in migrations/ folder.
Apply Migrations
python manage.py migrate
This applies migrations to the database.
Migration Workflow
- Modify
models.py - Run
makemigrations - Run
migrate - Repeat as needed
Working with Models
Creating Objects
from myapp.models import Post
# Method 1: Create and save
post = Post(title='My First Post', content='Hello, World!')
post.save()
# Method 2: Create directly
post = Post.objects.create(
title='My First Post',
content='Hello, World!'
)
Retrieving Objects
# Get all posts
all_posts = Post.objects.all()
# Get one post
post = Post.objects.get(id=1)
# Filter posts
published_posts = Post.objects.filter(status='published')
# Get first/last
first_post = Post.objects.first()
last_post = Post.objects.last()
Updating Objects
# Get post
post = Post.objects.get(id=1)
# Update
post.title = 'Updated Title'
post.save()
# Or update directly
Post.objects.filter(id=1).update(title='Updated Title')
Deleting Objects
# Delete one
post = Post.objects.get(id=1)
post.delete()
# Delete multiple
Post.objects.filter(status='draft').delete()
Querying the Database
Filtering
# Exact match
Post.objects.filter(title='My Post')
# Case-insensitive
Post.objects.filter(title__iexact='my post')
# Contains
Post.objects.filter(title__contains='Django')
# Greater than / Less than
Post.objects.filter(created_at__gte=datetime.now())
# In list
Post.objects.filter(status__in=['published', 'draft'])
Excluding
# Exclude
Post.objects.exclude(status='draft')
Ordering
# Order by
Post.objects.all().order_by('-created_at') # Newest first
Post.objects.all().order_by('title') # Alphabetical
Limiting
# First 5
Post.objects.all()[:5]
# Skip first 5, get next 5
Post.objects.all()[5:10]
Registering Models in Admin
Make models manageable in Django admin:
# myapp/admin.py
from django.contrib import admin
from .models import Post, Author
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'created_at']
list_filter = ['created_at', 'status']
search_fields = ['title', 'content']
admin.site.register(Author)
Complete Example
Models
# myapp/models.py
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)
def __str__(self):
return self.name
class Post(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
]
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
def get_excerpt(self, length=150):
return self.content[:length] + '...' if len(self.content) > length else self.content
Admin Configuration
# myapp/admin.py
from django.contrib import admin
from .models import Post, Category
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'category', 'status', 'created_at']
list_filter = ['status', 'created_at', 'category']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']
prepopulated_fields = {'slug': ('name',)}
Best Practices
- Use descriptive field names:
created_atnotdate - Set appropriate max_length: Don't use unnecessarily large values
- Use choices for limited options: Better than free text
- Add str methods: Makes admin and shell more readable
- Use migrations: Never edit database directly
- Index frequently queried fields: For performance
Common Mistakes
1. Forgetting to Run Migrations
# ❌ Wrong
# Changed models.py but didn't run makemigrations
# ✅ Correct
python manage.py makemigrations
python manage.py migrate
2. Not Adding str Method
# ❌ Wrong
class Post(models.Model):
title = models.CharField(max_length=200)
# No __str__ method
# ✅ Correct
class Post(models.Model):
title = models.CharField(max_length=200)
def __str__(self):
return self.title
3. Wrong on_delete Option
# ❌ Wrong - might cause errors
author = models.ForeignKey(Author, on_delete=models.DO_NOTHING)
# ✅ Correct
author = models.ForeignKey(Author, on_delete=models.CASCADE)
Practice Exercise
Create models for a blog:
Categorymodel with name and slugPostmodel with title, content, author, category, status, and timestamps- Add
__str__methods to both - Create and run migrations
- Register models in admin
- Create some sample data in Django shell
What's Next?
Now that you understand models, learn to:
- Create Views - Display data to users
- Build Templates - Create HTML pages
- Handle URLs - Route requests to views
- Work with Forms - Process user input
Previous Tutorial: Setting Up Django
Next Tutorial: Views and URLs