Django

The Web framework for perfectionists with deadlines - a full-stack framework with batteries included. Built-in ORM, admin interface, and authentication features.

PythonFrameworkBackendWebORMMVTAdmin Interface

GitHub Overview

django/django

The Web framework for perfectionists with deadlines.

Stars84,248
Watchers2,284
Forks32,718
Created:April 28, 2012
Language:Python
License:BSD 3-Clause "New" or "Revised" License

Topics

appsdjangoframeworkmodelsormpythontemplatesviewsweb

Star History

django/django Star History
Data as of: 7/17/2025, 10:32 AM

Framework

Django

Overview

Django is a high-level Python web framework that enables rapid development with practical, clean design principles.

Details

Django is a web framework written in Python that was originally developed in 2005 at a newspaper company in Lawrence, Kansas, and was open-sourced by the Django Software Foundation in 2008. Based on the "batteries included" philosophy, it provides all the tools needed for web development out of the box. Django follows a Model-View-Template (MVT) architecture and includes an Object-Relational Mapping (ORM) that abstracts database operations, an automatically generated admin interface, URL routing, template engine, form processing, authentication system, and security features. Designed with scalability and security in mind, Django is used by large-scale services like Instagram, Pinterest, Mozilla, and NASA. Following the "Don't Repeat Yourself" (DRY) principle and "Convention over Configuration" approach, it enables efficient development.

Pros and Cons

Pros

  • Comprehensive Framework: Rich built-in features including authentication, admin interface, ORM, and more
  • High Security: Built-in protection against SQL injection, XSS, CSRF, and other vulnerabilities
  • Automatic Admin Interface: Auto-generated admin interface from database models
  • Excellent ORM: Database abstraction improves maintainability and portability
  • Scalability: Well-suited for developing and operating large-scale web applications
  • Rich Documentation: Detailed and well-written official documentation
  • Active Community: Large community and extensive third-party packages

Cons

  • Learning Curve: High barrier to entry for beginners due to comprehensive features
  • Weight: Excessive functionality for small projects
  • Flexibility Constraints: Must follow framework conventions
  • Performance: May be slower than Ruby on Rails in some cases
  • Monolithic Structure: Not ideal for microservices architecture

Key Links

Code Examples

Hello World

# urls.py
from django.contrib import admin
from django.urls import path
from django.http import HttpResponse

def hello_world(request):
    return HttpResponse("Hello, Django World!")

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', hello_world, name='hello'),
]

# settings.py (basic configuration)
SECRET_KEY = 'your-secret-key-here'
DEBUG = True
ALLOWED_HOSTS = []

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

# Project creation commands
# django-admin startproject myproject
# cd myproject
# python manage.py runserver

Model Definition and ORM

# models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        verbose_name_plural = "categories"
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    tags = models.ManyToManyField('Tag', blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_published = models.BooleanField(default=False)
    
    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return self.title

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    
    def __str__(self):
        return self.name

# ORM usage examples
# Create and run migrations
# python manage.py makemigrations
# python manage.py migrate

# Database operations
from myapp.models import Post, Category, Tag

# Create data
category = Category.objects.create(name="Technology", description="Tech-related articles")
post = Post.objects.create(
    title="Getting Started with Django",
    content="Django is an amazing framework.",
    author_id=1,
    category=category,
    is_published=True
)

# Query examples
all_posts = Post.objects.all()
published_posts = Post.objects.filter(is_published=True)
recent_posts = Post.objects.filter(created_at__gte='2024-01-01')
tech_posts = Post.objects.filter(category__name="Technology")

# Complex queries
from django.db.models import Q, Count
popular_posts = Post.objects.annotate(
    tag_count=Count('tags')
).filter(
    Q(is_published=True) & Q(tag_count__gt=2)
).order_by('-created_at')

URL Routing

# myproject/urls.py (main URLconf)
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
    path('api/', include('api.urls')),
]

# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    # Basic URLs
    path('', views.index, name='index'),
    path('posts/', views.post_list, name='post_list'),
    
    # URLs with parameters
    path('posts/<int:post_id>/', views.post_detail, name='post_detail'),
    path('category/<slug:category_slug>/', views.category_posts, name='category_posts'),
    
    # Regular expression URLs
    path('archive/<int:year>/<int:month>/', views.archive, name='archive'),
    
    # Named groups
    path('tag/<str:tag_name>/', views.posts_by_tag, name='posts_by_tag'),
]

# views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from .models import Post, Category

def index(request):
    recent_posts = Post.objects.filter(is_published=True)[:5]
    return render(request, 'blog/index.html', {'posts': recent_posts})

def post_detail(request, post_id):
    post = get_object_or_404(Post, id=post_id, is_published=True)
    return render(request, 'blog/post_detail.html', {'post': post})

def category_posts(request, category_slug):
    category = get_object_or_404(Category, slug=category_slug)
    posts = Post.objects.filter(category=category, is_published=True)
    return render(request, 'blog/category_posts.html', {
        'category': category,
        'posts': posts
    })

Template Processing

<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Blog{% endblock %}</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <header>
        <nav>
            <a href="{% url 'blog:index' %}">Home</a>
            <a href="{% url 'blog:post_list' %}">All Posts</a>
        </nav>
    </header>
    
    <main>
        {% block content %}
        {% endblock %}
    </main>
    
    <footer>
        <p>&copy; 2024 My Blog</p>
    </footer>
</body>
</html>

<!-- templates/blog/index.html -->
{% extends 'base.html' %}
{% load static %}

{% block title %}Home - {{ block.super }}{% endblock %}

{% block content %}
<h1>Latest Posts</h1>

{% if posts %}
    {% for post in posts %}
    <article class="post-preview">
        <h2>
            <a href="{% url 'blog:post_detail' post.id %}">{{ post.title }}</a>
        </h2>
        <p class="post-meta">
            Author: {{ post.author.username }} | 
            Category: 
            {% if post.category %}
                <a href="{% url 'blog:category_posts' post.category.slug %}">
                    {{ post.category.name }}
                </a>
            {% else %}
                Uncategorized
            {% endif %} | 
            {{ post.created_at|date:"F j, Y" }}
        </p>
        <p>{{ post.content|truncatewords:30 }}</p>
        
        {% if post.tags.all %}
        <div class="tags">
            Tags: 
            {% for tag in post.tags.all %}
                <a href="{% url 'blog:posts_by_tag' tag.name %}" class="tag">
                    {{ tag.name }}
                </a>
                {% if not forloop.last %}, {% endif %}
            {% endfor %}
        </div>
        {% endif %}
    </article>
    {% endfor %}
{% else %}
    <p>No posts available yet.</p>
{% endif %}

<!-- Using custom filters -->
<p>{{ post.content|linebreaks|safe }}</p>
<p>Posted {{ post.created_at|timesince }} ago</p>
{% endblock %}

Form Processing

# forms.py
from django import forms
from django.contrib.auth.models import User
from .models import Post, Category

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'tags', 'is_published']
        widgets = {
            'title': forms.TextInput(attrs={
                'class': 'form-control',
                'placeholder': 'Enter title'
            }),
            'content': forms.Textarea(attrs={
                'class': 'form-control',
                'rows': 10,
                'placeholder': 'Enter content'
            }),
            'category': forms.Select(attrs={'class': 'form-control'}),
            'tags': forms.CheckboxSelectMultiple(),
            'is_published': forms.CheckboxInput(attrs={'class': 'form-check-input'})
        }
    
    def clean_title(self):
        title = self.cleaned_data['title']
        if len(title) < 5:
            raise forms.ValidationError('Title must be at least 5 characters long.')
        return title
    
    def clean_content(self):
        content = self.cleaned_data['content']
        if len(content) < 50:
            raise forms.ValidationError('Content must be at least 50 characters long.')
        return content

class ContactForm(forms.Form):
    name = forms.CharField(
        max_length=100,
        label='Name',
        widget=forms.TextInput(attrs={
            'class': 'form-control',
            'placeholder': 'Enter your name'
        })
    )
    email = forms.EmailField(
        label='Email',
        widget=forms.EmailInput(attrs={
            'class': 'form-control',
            'placeholder': '[email protected]'
        })
    )
    subject = forms.CharField(
        max_length=200,
        label='Subject',
        widget=forms.TextInput(attrs={'class': 'form-control'})
    )
    message = forms.CharField(
        label='Message',
        widget=forms.Textarea(attrs={
            'class': 'form-control',
            'rows': 6,
            'placeholder': 'Enter your message'
        })
    )

# views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.core.mail import send_mail
from .forms import PostForm, ContactForm

@login_required
def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            form.save_m2m()  # Save Many-to-Many fields
            messages.success(request, 'Post created successfully.')
            return redirect('blog:post_detail', post_id=post.id)
    else:
        form = PostForm()
    
    return render(request, 'blog/create_post.html', {'form': form})

def contact(request):
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            # Send email
            name = form.cleaned_data['name']
            email = form.cleaned_data['email']
            subject = form.cleaned_data['subject']
            message = form.cleaned_data['message']
            
            full_message = f"""
            Name: {name}
            Email: {email}
            
            Message:
            {message}
            """
            
            send_mail(
                subject=f"[Contact] {subject}",
                message=full_message,
                from_email=email,
                recipient_list=['[email protected]'],
                fail_silently=False,
            )
            
            messages.success(request, 'Contact message sent successfully.')
            return redirect('blog:contact')
    else:
        form = ContactForm()
    
    return render(request, 'blog/contact.html', {'form': form})

Admin Interface

# admin.py
from django.contrib import admin
from django.utils.html import format_html
from .models import Post, Category, Tag

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'post_count', 'created_at']
    search_fields = ['name', 'description']
    list_filter = ['created_at']
    ordering = ['name']
    
    def post_count(self, obj):
        return obj.post_set.count()
    post_count.short_description = 'Post Count'

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ['name', 'post_count']
    search_fields = ['name']
    
    def post_count(self, obj):
        return obj.post_set.count()
    post_count.short_description = 'Usage Count'

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = [
        'title', 'author', 'category', 'is_published', 
        'created_at', 'view_on_site'
    ]
    list_filter = [
        'is_published', 'created_at', 'category', 'tags'
    ]
    search_fields = ['title', 'content', 'author__username']
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    
    # Layout for detail view
    fieldsets = (
        ('Basic Information', {
            'fields': ('title', 'content')
        }),
        ('Classification', {
            'fields': ('category', 'tags'),
            'classes': ('collapse',)
        }),
        ('Settings', {
            'fields': ('is_published',),
            'classes': ('collapse',)
        }),
        ('Meta Information', {
            'fields': ('author', 'created_at', 'updated_at'),
            'classes': ('collapse',),
        }),
    )
    
    # Read-only fields
    readonly_fields = ['created_at', 'updated_at']
    
    # Custom queryset
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)
    
    # Custom save behavior
    def save_model(self, request, obj, form, change):
        if not change:  # New object
            obj.author = request.user
        super().save_model(request, obj, form, change)
    
    # Custom actions
    def make_published(self, request, queryset):
        count = queryset.update(is_published=True)
        self.message_user(request, f'{count} posts were published.')
    make_published.short_description = 'Publish selected posts'
    
    def make_unpublished(self, request, queryset):
        count = queryset.update(is_published=False)
        self.message_user(request, f'{count} posts were unpublished.')
    make_unpublished.short_description = 'Unpublish selected posts'
    
    actions = [make_published, make_unpublished]
    
    # Custom display
    def view_on_site(self, obj):
        if obj.is_published:
            return format_html(
                '<a href="/posts/{}/" target="_blank">View on Site</a>',
                obj.id
            )
        return '-'
    view_on_site.short_description = 'Site View'

# Customize admin site
admin.site.site_header = 'My Blog Admin'
admin.site.site_title = 'Blog Admin'
admin.site.index_title = 'Dashboard'

# Create superuser command
# python manage.py createsuperuser