Django Beginner Tutorial: Create Your First Django Project
Introduction
Django is a powerful, high-level Python web framework that enables rapid development of secure and maintainable websites. Built by experienced developers, Django takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel.
This comprehensive beginner tutorial will walk you through creating your first Django project from scratch. You'll learn how to set up Django, create apps, work with models, views, templates, and build a functional web application. By the end of this tutorial, you'll have a solid foundation in Django development.
Prerequisites
Before starting, make sure you have:
- installed on your system
- (Python package installer)
- (variables, functions, classes)
- (VS Code, PyCharm, or any editor you prefer)
- access
You can check your Python version by running:
python --version
# or
python3 --version
Step 1: Setting Up Your Environment
First, let's create a virtual environment to isolate your Django project dependencies:
# Create project directory
mkdir django-blog
cd django-blog
# Create virtual environment
py -m venv venv
# Activate virtual environment
venv\Scripts\Activate.ps1
# Create project directory
mkdir django-blog
cd django-blog
# Create virtual environment
python3 -m venv venv
# Activate virtual environment
source venv/bin/activate
Once activated, you'll see `(venv)` in your terminal prompt. This indicates your virtual environment is active.
pip install django
# Verify installation
python -m django --version
You should see the Django version number (e.g., 4.2.x or 5.0.x).
Step 2: Creating Your First Django Project
Now let's create a new Django project:
# Create Django project
django-admin startproject myblog
# Navigate into project directory
cd myblog
This creates a `myblog` directory with the following structure:
myblog/
manage.py
myblog/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
Understanding the Project Structure:
- Django's command-line utility for administrative tasks
- Configuration file for your Django project
- URL routing configuration
- WSGI configuration for deployment
- ASGI configuration for async support
Run the Development Server:
python manage.py runserver
Open your browser and visit `http://127.0.0.1:8000/`. You should see the Django welcome page!
Step 3: Creating Your First Django App
In Django, a project contains multiple apps. Each app handles a specific functionality. Let's create a blog app:
# Create a new app
python manage.py startapp blog
This creates a `blog` directory with:
blog/
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
migrations/
Understanding App Structure:
- Define your database models
- Handle request/response logic
- Register models for Django admin
- URL routing for the app (create this)
- Database migration files
Register the App:
Open `myblog/settings.py` and add your app to `INSTALLED_APPS`:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # Add your app here
]
Step 4: Creating Your First Model
Models define the structure of your database. Let's create a Post model for our blog:
Open `blog/models.py` and add:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
published_at = models.DateTimeField(null=True, blank=True)
status = models.CharField(
max_length=10,
choices=[('draft', 'Draft'), ('published', 'Published')],
default='draft'
)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.title
def publish(self):
self.published_at = timezone.now()
self.status = 'published'
self.save()
Understanding the Model:
- For short text fields (title, status)
- For longer text content
- URL-friendly version of title
- Relationship to User model
- For dates and times
- Set when object is created
- Update on every save
Create and Apply Migrations:
# Create migration files
python manage.py makemigrations
# Apply migrations to database
python manage.py migrate
This creates the database tables for your models.
Step 5: Working with Django Admin
Django provides a powerful admin interface. Let's register our Post model:
Open `blog/admin.py`:
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'status', 'created_at', 'published_at']
list_filter = ['status', 'created_at', 'published_at']
search_fields = ['title', 'content']
prepopulated_fields = {'slug': ('title',)}
date_hierarchy = 'created_at'
ordering = ['-created_at']
python manage.py createsuperuser
Step 6: Creating Views
Views handle the request/response logic. Let's create views for listing and viewing posts:
Open `blog/views.py`:
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(
status='published',
published_at__lte=timezone.now()
).order_by('-published_at')
context = {
'posts': posts
}
return render(request, 'blog/post_list.html', context)
def post_detail(request, slug):
post = get_object_or_404(
Post,
slug=slug,
status='published',
published_at__lte=timezone.now()
)
context = {
'post': post
}
return render(request, 'blog/post_detail.html', context)
Understanding Views:
- Displays all published posts
- Shows a single post by slug
- Returns 404 if post not found
- Renders template with context
- QuerySet filtering
- Less than or equal filter
Class-Based Views (Alternative):
from django.views.generic import ListView, DetailView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'
def get_queryset(self):
return Post.objects.filter(
status='published',
published_at__lte=timezone.now()
).order_by('-published_at')
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
slug_field = 'slug'
slug_url_kwarg = 'slug'
Step 7: Creating Templates
Templates define the HTML structure. Create a templates directory:
# In your blog app directory
mkdir -p blog/templates/blog
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog Posts</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.post {
border-bottom: 1px solid #ddd;
padding: 20px 0;
}
.post h2 a {
color: #333;
text-decoration: none;
}
.post h2 a:hover {
color: #007bff;
}
.meta {
color: #666;
font-size: 0.9em;
}
</style>
</head>
<body>
<h1>My Blog</h1>
{% for post in posts %}
<div class="post">
<h2><a href="{% url 'post_detail' post.slug %}">{{ post.title }}</a></h2>
<p class="meta">
Published on {{ post.published_at|date:"F d, Y" }} by {{ post.author.username }}
</p>
<p>{{ post.content|truncatewords:30 }}</p>
<a href="{% url 'post_detail' post.slug %}">Read more →</a>
</div>
{% empty %}
<p>No posts available.</p>
{% endfor %}
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ post.title }}</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
.meta {
color: #666;
font-size: 0.9em;
margin-bottom: 20px;
}
.content {
margin-top: 20px;
}
a {
color: #007bff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<a href="{% url 'post_list' %}">← Back to Posts</a>
<h1>{{ post.title }}</h1>
<p class="meta">
Published on {{ post.published_at|date:"F d, Y" }} by {{ post.author.username }}
</p>
<div class="content">
{{ post.content|linebreaks }}
</div>
</body>
</html>
Step 8: Setting Up URLs
URLs map URLs to views. Create `blog/urls.py`:
from django.urls import path
from . import views
app_name = 'blog'
urlpatterns = [
path('', views.post_list, name='post_list'),
path('<slug:slug>/', views.post_detail, name='post_detail'),
]
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
Understanding URLs:
- Define URL patterns
- Capture slug parameter
- Include app URLs
- Namespace for URLs
- Name for URL reversing
Test Your URLs:
python manage.py runserver
Visit:
- `http://127.0.0.1:8000/` - Post list
- `http://127.0.0.1:8000/admin/` - Admin panel
- `http://127.0.0.1:8000/your-post-slug/` - Post detail
Step 9: Working with Forms
Django makes form handling easy. Create `blog/forms.py`:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'slug', 'content', 'status']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'slug': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
'status': forms.Select(attrs={'class': 'form-control'}),
}
# In blog/views.py
from django.shortcuts import redirect
from django.contrib.auth.decorators import login_required
from .forms import PostForm
@login_required
def post_create(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', slug=post.slug)
else:
form = PostForm()
return render(request, 'blog/post_form.html', {'form': form})
<!-- blog/templates/blog/post_form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Create Post</title>
</head>
<body>
<h1>Create New Post</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Create Post</button>
</form>
</body>
</html>
# In blog/urls.py
path('create/', views.post_create, name='post_create'),
Step 10: Static Files and Media Files
Django handles static files (CSS, JS, images) and media files (user uploads).
# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
mkdir -p blog/static/blog/css
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 900px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.post {
background: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
{% load static %}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="{% static 'blog/css/style.css' %}">
</head>
<body>
<!-- Your content -->
</body>
</html>
Step 11: Database Queries and ORM
Django ORM provides powerful database querying. Here are common operations:
# Get all posts
all_posts = Post.objects.all()
# Get a single post
post = Post.objects.get(id=1)
# Filter posts
published_posts = Post.objects.filter(status='published')
# Exclude posts
draft_posts = Post.objects.exclude(status='published')
# Order posts
ordered_posts = Post.objects.order_by('-created_at')
# Limit results
recent_posts = Post.objects.all()[:5]
# Count posts
post_count = Post.objects.count()
# Check if exists
exists = Post.objects.filter(title='My Post').exists()
# Complex filtering
posts = Post.objects.filter(
status='published',
created_at__gte=timezone.now() - timedelta(days=7)
)
# Lookups
posts = Post.objects.filter(title__icontains='django')
posts = Post.objects.filter(created_at__year=2024)
posts = Post.objects.filter(author__username='admin')
# Q objects for complex queries
from django.db.models import Q
posts = Post.objects.filter(
Q(title__icontains='django') | Q(content__icontains='python')
)
# Aggregations
from django.db.models import Count, Avg
Post.objects.aggregate(Count('id'))
Post.objects.filter(status='published').count()
# Annotations
from django.db.models import Count
posts = Post.objects.annotate(
comment_count=Count('comments')
)
# select_related (for ForeignKey)
posts = Post.objects.select_related('author').all()
# prefetch_related (for ManyToMany, reverse ForeignKey)
posts = Post.objects.prefetch_related('tags').all()
# only() - fetch only specified fields
posts = Post.objects.only('title', 'created_at')
# defer() - exclude specified fields
posts = Post.objects.defer('content')
Step 12: User Authentication
Django includes a built-in authentication system. Let's add login/logout functionality:
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
</body>
</html>
# In blog/views.py
from django.contrib.auth import login
from django.contrib.auth.forms import AuthenticationForm
from django.shortcuts import redirect
def login_view(request):
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('post_list')
else:
form = AuthenticationForm()
return render(request, 'blog/login.html', {'form': form})
# In blog/urls.py
from django.contrib.auth import views as auth_views
urlpatterns = [
path('login/', views.login_view, name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),
# ... other URLs
]
from django.contrib.auth.decorators import login_required
@login_required
def post_create(request):
# Only logged-in users can access
pass
{% if user.is_authenticated %}
<p>Welcome, {{ user.username }}!</p>
<a href="{% url 'logout' %}">Logout</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
Step 13: Testing Your Application
Django includes a built-in testing framework. Create tests in `blog/tests.py`:
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.utils import timezone
from .models import Post
class PostModelTest(TestCase):
def setUp(self):
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.post = Post.objects.create(
title='Test Post',
slug='test-post',
content='Test content',
author=self.user,
status='published',
published_at=timezone.now()
)
def test_post_str(self):
self.assertEqual(str(self.post), 'Test Post')
def test_post_publish(self):
self.post.publish()
self.assertEqual(self.post.status, 'published')
self.assertIsNotNone(self.post.published_at)
class PostViewTest(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.post = Post.objects.create(
title='Test Post',
slug='test-post',
content='Test content',
author=self.user,
status='published',
published_at=timezone.now()
)
def test_post_list_view(self):
response = self.client.get('/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Test Post')
def test_post_detail_view(self):
response = self.client.get('/test-post/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Test content')
# Run all tests
python manage.py test
# Run specific test
python manage.py test blog.tests.PostModelTest
# Run with verbosity
python manage.py test --verbosity=2
Step 14: Deployment Preparation
Before deploying, configure your project for production:
# Security settings
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']
# Database (use PostgreSQL in production)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'your_db_name',
'USER': 'your_db_user',
'PASSWORD': 'your_db_password',
'HOST': 'localhost',
'PORT': '5432',
}
}
# Static files
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
# Security middleware
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
python manage.py collectstatic
pip freeze > requirements.txt
Common Deployment Platforms:
- Easy deployment with Git
- VPS with Django setup
- Elastic Beanstalk or EC2
- Django hosting
- Modern deployment platform
Deployment Checklist:
- ✅ Set `DEBUG = False`
- ✅ Configure `ALLOWED_HOSTS`
- ✅ Set up production database
- ✅ Configure static files
- ✅ Set up environment variables
- ✅ Enable HTTPS
- ✅ Configure security settings
- ✅ Set up error logging
- ✅ Configure email settings
Common Django Commands Cheat Sheet
Here's a quick reference for common Django commands:
# Create project
django-admin startproject projectname
# Create app
python manage.py startapp appname
# Run development server
python manage.py runserver
# Run on specific port
python manage.py runserver 8080
# Create migrations
python manage.py makemigrations
# Apply migrations
python manage.py migrate
# Show migration status
python manage.py showmigrations
# Rollback migration
python manage.py migrate appname migration_number
# Create superuser
python manage.py createsuperuser
# Open Django shell
python manage.py shell
# Open shell with IPython
python manage.py shell -i ipython
# Dump data
python manage.py dumpdata appname > data.json
# Load data
python manage.py loaddata data.json
# Collect static files
python manage.py collectstatic
# Find static files
python manage.py findstatic css/style.css
# Run all tests
python manage.py test
# Run specific app tests
python manage.py test blog
# Run with coverage
coverage run --source='.' manage.py test
coverage report
# Check for issues
python manage.py check
# Show URL patterns
python manage.py show_urls
# Create custom management command
python manage.py startapp appname
# Then create appname/management/commands/commandname.py
Next Steps and Learning Resources
Congratulations! You've built your first Django application. Here's what to learn next:
Advanced Topics:
- REST APIs: Use Django REST Framework - Check out our Django REST Framework Tutorial
- Real-time Features: WebSockets with Django Channels
- Caching: Redis and Memcached integration
- Celery: Background task processing
- Search: Full-text search with Elasticsearch
- API Integration: Third-party API integration
- Advanced Queries: Complex database queries
- Custom Managers: Custom QuerySets
- Signals: Django signals for events
- Middleware: Custom middleware
Best Practices:
- Project Structure: Organize large projects
- Code Organization: Follow Django conventions
- Security: Implement security best practices
- Performance: Optimize queries and caching
- Testing: Write comprehensive tests
- Documentation: Document your code
Learning Resources:
- Official Django Documentation: https://docs.djangoproject.com/
- Django Tutorial: Official tutorial on Django website
- Django for Beginners: Book by William S. Vincent
- Two Scoops of Django: Best practices book
- Django Girls Tutorial: Beginner-friendly tutorial
- Real Python: Django tutorials and articles
Community:
- Django Forum: Community discussions
- Stack Overflow: Q&A platform
- Django Discord: Real-time chat
- Reddit r/django: Community discussions
Conclusion
You've successfully created your first Django project! You've learned:
- ✅ Setting up Django environment
- ✅ Creating projects and apps
- ✅ Working with models and databases
- ✅ Creating views and templates
- ✅ URL routing
- ✅ Django admin interface
- ✅ Forms and user input
- ✅ Static files
- ✅ User authentication
- ✅ Testing
- ✅ Deployment preparation
Key Takeaways:
- Django follows the "batteries-included" philosophy
- Models define your database structure
- Views handle request/response logic
- Templates render HTML
- URLs map URLs to views
- Django admin provides powerful admin interface
- ORM makes database queries easy
- Testing is built-in and encouraged
Remember:
- Practice is key to mastering Django
- Read the official documentation
- Join the Django community
- Build projects to learn
- Follow Django best practices
- Keep learning and improving Django is a powerful framework that can help you build complex web applications efficiently. Continue practicing, building projects, and exploring advanced features. Happy coding!