Backend

Getting Started with Django: Python Web Framework

Getting Started with Django: Python Web Framework
Dimuthu Wayaman
Dimuthu Wayaman
December 27, 2025
16 min read
PythonDjangoBackendWeb FrameworkTutorial

Getting Started with Django: Python Web Framework

Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Built by experienced developers, it takes care of much of the hassle of web development.

Why Django?

  1. Batteries Included: Admin panel, ORM, authentication built-in
  2. Security: Protection against SQL injection, XSS, CSRF
  3. Scalable: Powers Instagram, Pinterest, Disqus
  4. Versatile: APIs, web apps, content management
  5. Great Documentation: Extensive docs and tutorials

Installation and Setup

# Create virtual environment python -m venv venv source venv/bin/activate # Windows: venv\\Scripts\\activate # Install Django pip install django # Create project django-admin startproject myproject cd myproject # Create app python manage.py startapp blog # Run development server python manage.py runserver

Project Structure

myproject/
├── myproject/
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── asgi.py
│   └── wsgi.py
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── templates/
├── static/
└── manage.py

Creating Models

# blog/models.py from django.db import models from django.contrib.auth.models import User from django.urls import reverse from django.utils import timezone class Category(models.Model): name = models.CharField(max_length=100) slug = models.SlugField(unique=True) description = models.TextField(blank=True) class Meta: verbose_name_plural = "categories" ordering = ['name'] 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) author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts') category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='posts') content = models.TextField() excerpt = models.TextField(max_length=500, blank=True) featured_image = models.ImageField(upload_to='blog/', blank=True) status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published_at = models.DateTimeField(default=timezone.now) class Meta: ordering = ['-published_at'] def __str__(self): return self.title def get_absolute_url(self): return reverse('blog:post_detail', kwargs={'slug': self.slug}) class Comment(models.Model): post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments') author = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) is_approved = models.BooleanField(default=False) class Meta: ordering = ['-created_at'] def __str__(self): return "Comment by {} on {}".format(self.author.username, self.post.title)

Creating Views

# blog/views.py from django.shortcuts import render, get_object_or_404, redirect from django.views.generic import ListView, DetailView, CreateView, UpdateView from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.decorators import login_required from django.contrib import messages from django.db.models import Q from .models import Post, Category, Comment from .forms import PostForm, CommentForm class PostListView(ListView): model = Post template_name = 'blog/post_list.html' context_object_name = 'posts' paginate_by = 10 def get_queryset(self): queryset = Post.objects.filter(status='published') category = self.request.GET.get('category') search = self.request.GET.get('search') if category: queryset = queryset.filter(category__slug=category) if search: queryset = queryset.filter( Q(title__icontains=search) | Q(content__icontains=search) | Q(excerpt__icontains=search) ) return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['categories'] = Category.objects.all() return context class PostDetailView(DetailView): model = Post template_name = 'blog/post_detail.html' context_object_name = 'post' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['comments'] = self.object.comments.filter(is_approved=True) context['comment_form'] = CommentForm() return context class PostCreateView(LoginRequiredMixin, CreateView): model = Post form_class = PostForm template_name = 'blog/post_form.html' def form_valid(self, form): form.instance.author = self.request.user messages.success(self.request, 'Post created successfully!') return super().form_valid(form) @login_required def add_comment(request, post_slug): post = get_object_or_404(Post, slug=post_slug) if request.method == 'POST': form = CommentForm(request.POST) if form.is_valid(): comment = form.save(commit=False) comment.post = post comment.author = request.user comment.save() messages.success(request, 'Comment submitted for approval.') return redirect('blog:post_detail', slug=post_slug)

Admin Configuration

# blog/admin.py from django.contrib import admin from .models import Post, Category, Comment @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ['name', 'slug'] prepopulated_fields = {'slug': ('name',)} @admin.register(Post) class PostAdmin(admin.ModelAdmin): list_display = ['title', 'author', 'category', 'status', 'published_at'] list_filter = ['status', 'category', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} date_hierarchy = 'published_at' ordering = ['-published_at'] raw_id_fields = ['author'] @admin.register(Comment) class CommentAdmin(admin.ModelAdmin): list_display = ['author', 'post', 'created_at', 'is_approved'] list_filter = ['is_approved', 'created_at'] actions = ['approve_comments'] def approve_comments(self, request, queryset): queryset.update(is_approved=True) approve_comments.short_description = "Approve selected comments"

URL Configuration

# blog/urls.py from django.urls import path from . import views app_name = 'blog' urlpatterns = [ path('', views.PostListView.as_view(), name='post_list'), path('post/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail'), path('post/new/', views.PostCreateView.as_view(), name='post_create'), path('post/<slug:post_slug>/comment/', views.add_comment, name='add_comment'), ] # myproject/urls.py from django.contrib import admin from django.urls import path, include from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls', namespace='blog')), ] if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Templates

<!-- templates/blog/post_list.html --> {% extends 'base.html' %} {% block content %} <div class="container"> <h1>Blog Posts</h1> <!-- Search Form --> <form method="get" class="search-form"> <input type="text" name="search" placeholder="Search posts..." value="{{ request.GET.search }}"> <button type="submit">Search</button> </form> <!-- Category Filter --> <div class="categories"> <a href="{% url 'blog:post_list' %}">All</a> {% for category in categories %} <a href="?category={{ category.slug }}">{{ category.name }}</a> {% endfor %} </div> <!-- Posts Grid --> <div class="posts-grid"> {% for post in posts %} <article class="post-card"> {% if post.featured_image %} <img src="{{ post.featured_image.url }}" alt="{{ post.title }}"> {% endif %} <div class="post-content"> <span class="category">{{ post.category.name }}</span> <h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2> <p>{{ post.excerpt|truncatewords:30 }}</p> <div class="meta"> <span>By {{ post.author.username }}</span> <span>{{ post.published_at|date:"M d, Y" }}</span> </div> </div> </article> {% empty %} <p>No posts found.</p> {% endfor %} </div> <!-- Pagination --> {% if page_obj.has_other_pages %} <div class="pagination"> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}">Previous</a> {% endif %} <span>Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span> {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">Next</a> {% endif %} </div> {% endif %} </div> {% endblock %}

Conclusion

Django provides everything you need to build web applications quickly and securely. Key takeaways:

  1. Use the MTV (Model-Template-View) pattern
  2. Leverage the built-in admin interface
  3. Use class-based views for common patterns
  4. Implement proper authentication and authorization
  5. Write tests for your application
  6. Deploy with WSGI servers like Gunicorn

Start building and explore Django's extensive documentation for more advanced features!

Dimuthu Wayaman

About Dimuthu Wayaman

Mobile Application Developer and UI Designer specializing in Flutter development. Passionate about creating beautiful, functional mobile applications and sharing knowledge with the developer community.