09/02/2026⏱️ 15 min read
Django Beginner Tutorial: Create Your First Django Project
DjangoPythonBeginnerTutorialWeb DevelopmentORMMVCDatabase

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:
  • Python 3.8+ installed on your system
  • pip (Python package installer)
  • Basic Python knowledge (variables, functions, classes)
  • A code editor (VS Code, PyCharm, or any editor you prefer)
  • Terminal/Command Prompt access
You can check your Python version by running:
bash
python --version # or python3 --version

Step 1: Setting Up Your Environment

First, let's create a virtual environment to isolate your Django project dependencies:
On Windows (PowerShell):
bash
# Create project directory mkdir django-blog cd django-blog # Create virtual environment py -m venv venv # Activate virtual environment venv\Scripts\Activate.ps1
On macOS/Linux:
bash
# 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.
Install Django:
bash
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:
bash
# 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:
  • manage.py: Django's command-line utility for administrative tasks
  • settings.py: Configuration file for your Django project
  • urls.py: URL routing configuration
  • wsgi.py: WSGI configuration for deployment
  • asgi.py: ASGI configuration for async support
Run the Development Server:
bash
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:
bash
# 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:
  • models.py: Define your database models
  • views.py: Handle request/response logic
  • admin.py: Register models for Django admin
  • urls.py: URL routing for the app (create this)
  • migrations/: Database migration files
Register the App: Open myblog/settings.py and add your app to INSTALLED_APPS:
python
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:
python
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:
  • CharField: For short text fields (title, status)
  • TextField: For longer text content
  • SlugField: URL-friendly version of title
  • ForeignKey: Relationship to User model
  • DateTimeField: For dates and times
  • auto_now_add: Set when object is created
  • auto_now: Update on every save
Create and Apply Migrations:
bash
# 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:
python
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']
Create a Superuser:
bash
python manage.py createsuperuser
Follow the prompts to create an admin user. Then run the server and visit http://127.0.0.1:8000/admin/ to access the admin panel.
Admin Features:
  • list_display: Fields to display in list view
  • list_filter: Filters in the sidebar
  • search_fields: Searchable fields
  • prepopulated_fields: Auto-generate slug from title
  • date_hierarchy: Date-based navigation
  • ordering: Default ordering

Step 6: Creating Views

Views handle the request/response logic. Let's create views for listing and viewing posts:
Open blog/views.py:
python
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:
  • post_list: Displays all published posts
  • post_detail: Shows a single post by slug
  • get_object_or_404: Returns 404 if post not found
  • render: Renders template with context
  • objects.filter(): QuerySet filtering
  • published_at__lte: Less than or equal filter
Class-Based Views (Alternative):
python
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:
bash
# In your blog app directory mkdir -p blog/templates/blog
Create blog/templates/blog/post_list.html:
html
<!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>
Create blog/templates/blog/post_detail.html:
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>
Django Template Tags:
  • {% for %}: Loop through items
  • {% if %}: Conditional statements
  • {% url %}: Generate URLs
  • {{ variable }}: Display variables
  • |filter: Apply template filters
  • |date: Format dates
  • |truncatewords: Truncate text
  • |linebreaks: Convert line breaks to HTML

Step 8: Setting Up URLs

URLs map URLs to views. Create blog/urls.py:
python
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'), ]
Update myblog/urls.py:
python
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include('blog.urls')), ]
Understanding URLs:
  • path(): Define URL patterns
  • <slug:slug>: Capture slug parameter
  • include(): Include app URLs
  • app_name: Namespace for URLs
  • name: Name for URL reversing
Test Your URLs:
bash
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:
python
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'}), }
Create a View for the Form:
python
# 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})
Create Template for Form:
html
<!-- 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>
Add URL:
python
# In blog/urls.py path('create/', views.post_create, name='post_create'),
Form Features:
  • ModelForm: Auto-generate form from model
  • widgets: Customize form fields
  • csrf_token: CSRF protection
  • is_valid(): Validate form data
  • save(commit=False): Save without committing

Step 10: Static Files and Media Files

Django handles static files (CSS, JS, images) and media files (user uploads).
Configure Static Files in settings.py:
python
# Static files (CSS, JavaScript, Images) STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' # Media files MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'media'
Update myblog/urls.py for Development:
python
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)
Create Static Files Directory:
bash
mkdir -p blog/static/blog/css
Create blog/static/blog/css/style.css:
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 Files in Templates:
html
{% 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:
Basic Queries:
python
# 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()
Advanced Queries:
python
# 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') )
Query Optimization:
python
# 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:
Create blog/templates/blog/login.html:
html
<!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>
Add Login View:
python
# 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})
Add URLs:
python
# 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 ]
Protect Views:
python
from django.contrib.auth.decorators import login_required @login_required def post_create(request): # Only logged-in users can access pass
Check Authentication in Templates:
html
{% 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:
python
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 Tests:
bash
# 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:
Update settings.py for Production:
python
# 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
Collect Static Files:
bash
python manage.py collectstatic
Create requirements.txt:
bash
pip freeze > requirements.txt
Common Deployment Platforms:
  • Heroku: Easy deployment with Git
  • DigitalOcean: VPS with Django setup
  • AWS: Elastic Beanstalk or EC2
  • PythonAnywhere: Django hosting
  • Railway: 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:
Project Management:
bash
# 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
Database Management:
bash
# 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
Shell and Database:
bash
# 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
Static Files:
bash
# Collect static files python manage.py collectstatic # Find static files python manage.py findstatic css/style.css
Testing:
bash
# 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
Other Useful Commands:
bash
# 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!

Share this article

Comments