Django’s ORM (Object-Relational Mapping) lets you define database tables as Python classes and query them without writing SQL.

Defining Models

  # blog/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    published = models.BooleanField(default=False)
    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

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    author = models.CharField(max_length=100)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f"Comment by {self.author} on {self.post.title}"
  

Migrations

Migrations track schema changes over time:

  python manage.py makemigrations
python manage.py migrate
  

Querying with the ORM

  from blog.models import Post, Comment

# Create
post = Post.objects.create(title="First Post", content="Hello world!")

# Read
all_posts = Post.objects.all()
published = Post.objects.filter(published=True)
one_post = Post.objects.get(pk=1)

# Update
post.title = "Updated Title"
post.save()

# Delete
post.delete()

# Complex queries
recent = Post.objects.filter(published=True).order_by('-created_at')[:5]
count = Post.objects.filter(published=True).count()

# Related objects
comments = post.comments.all()
Post.objects.select_related('author').prefetch_related('comments')
  

Field Types

Field Use Case
CharField Short text (requires max_length)
TextField Long text
IntegerField Integers
BooleanField True/False
DateTimeField Date and time
ForeignKey Many-to-one relationship
ManyToManyField Many-to-many relationship
JSONField JSON data

Admin Interface

Register models to manage them via the admin panel:

  # blog/admin.py
from django.contrib import admin
from .models import Post, Comment

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'published', 'created_at']
    list_filter = ['published', 'created_at']
    search_fields = ['title', 'content']

admin.site.register(Comment)
  

Database Configuration

Django uses SQLite by default. For PostgreSQL in production:

  # settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}
  

Install the driver: pip install psycopg2-binary

The ORM is one of Django’s greatest strengths — it keeps your database logic in Python and handles SQL generation for you.