0% found this document useful (0 votes)
7 views14 pages

Django Blog Guide

This document is a comprehensive step-by-step guide for beginners to create a Django blog project using SQLite, Tailwind CSS, and features like dark mode and profile pictures. It covers project setup, database models, forms, views, URL configuration, and templates, providing code snippets and instructions for each part. The guide aims to help users build a fully functional blog application with user registration and profile management.

Uploaded by

yabipo6667
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views14 pages

Django Blog Guide

This document is a comprehensive step-by-step guide for beginners to create a Django blog project using SQLite, Tailwind CSS, and features like dark mode and profile pictures. It covers project setup, database models, forms, views, URL configuration, and templates, providing code snippets and instructions for each part. The guide aims to help users build a fully functional blog application with user registration and profile management.

Uploaded by

yabipo6667
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Django Blog Project

Complete Step-by-Step Guide for Beginners


Stack: Django • SQLite • Tailwind CSS • Dark Mode • Profile Pictures

1. Project Setup
1.1 Create Project Structure
Open your terminal in Kali and run these commands one by one:
# Navigate to your desired folder (e.g. Desktop)
cd ~/Desktop

# Create the Django project


django-admin startproject myblog
cd myblog

# Create the blog app


python3 [Link] startapp blog

# Create the users app


python3 [Link] startapp users

1.2 Install Required Packages


Install Pillow for image uploads (profile pictures):
pip3 install Pillow

1.3 Configure [Link]


Open myblog/[Link] and make these changes:
# In INSTALLED_APPS, add your apps:
INSTALLED_APPS = [
'[Link]',
'[Link]',
'[Link]',
'[Link]',
'[Link]',
'[Link]',
'blog', # <-- add this
'users', # <-- add this
]

# At the bottom of [Link], add:


import os
MEDIA_URL = '/media/'
MEDIA_ROOT = [Link](BASE_DIR, 'media')
LOGIN_REDIRECT_URL = 'blog-home'
LOGIN_URL = 'login'

💡 Note: BASE_DIR is already defined near the top of [Link] — don't re-define it.

2. Database Models
2.1 Blog Post Model — blog/[Link]
Replace the contents of blog/[Link] with:
from [Link] import models
from [Link] import User
from [Link] import timezone

class Post([Link]):
title = [Link](max_length=200)
content = [Link]()
image = [Link](upload_to='post_pics/', blank=True, null=True)
author = [Link](User, on_delete=[Link])
created = [Link](default=[Link])

def __str__(self):
return [Link]

class Meta:
ordering = ['-created']

2.2 User Profile Model — users/[Link]


Replace the contents of users/[Link] with:
from [Link] import models
from [Link] import User

class Profile([Link]):
user = [Link](User, on_delete=[Link])
image = [Link](default='[Link]', upload_to='profile_pics/')

def __str__(self):
return f'{[Link]} Profile'

2.3 Auto-create Profile — users/[Link]


Create a new file users/[Link]:
from [Link] import post_save
from [Link] import User
from [Link] import receiver
from .models import Profile
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
[Link](user=instance)

@receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
[Link]()

2.4 Register Signals — users/[Link]


Edit users/[Link] so signals load automatically:
from [Link] import AppConfig

class UsersConfig(AppConfig):
default_auto_field = '[Link]'
name = 'users'

def ready(self):
import [Link]

2.5 Run Migrations


python3 [Link] makemigrations
python3 [Link] migrate

3. Forms
3.1 User Forms — users/[Link]
Create users/[Link]:
from django import forms
from [Link] import User
from [Link] import UserCreationForm
from .models import Profile

class RegisterForm(UserCreationForm):
email = [Link]()

class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']

class UserUpdateForm([Link]):
email = [Link]()

class Meta:
model = User
fields = ['username', 'email']

class ProfileUpdateForm([Link]):
class Meta:
model = Profile
fields = ['image']

3.2 Post Form — blog/[Link]


Create blog/[Link]:
from django import forms
from .models import Post

class PostForm([Link]):
class Meta:
model = Post
fields = ['title', 'content', 'image']

4. Views
4.1 Blog Views — blog/[Link]
from [Link] import render, get_object_or_404, redirect
from [Link] import login_required
from [Link] import User
from .models import Post
from .forms import PostForm

def home(request):
posts = [Link]()
return render(request, 'blog/[Link]', {'posts': posts})

@login_required
def post_create(request):
if [Link] == 'POST':
form = PostForm([Link], [Link])
if form.is_valid():
post = [Link](commit=False)
[Link] = [Link]
[Link]()
return redirect('blog-home')
else:
form = PostForm()
return render(request, 'blog/post_form.html', {'form': form, 'title': 'New
Post'})

@login_required
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if [Link] != [Link]:
return redirect('blog-home')
if [Link] == 'POST':
form = PostForm([Link], [Link], instance=post)
if form.is_valid():
[Link]()
return redirect('blog-home')
else:
form = PostForm(instance=post)
return render(request, 'blog/post_form.html', {'form': form, 'title': 'Edit
Post'})

@login_required
def post_delete(request, pk):
post = get_object_or_404(Post, pk=pk)
if [Link] == [Link]:
[Link]()
return redirect('blog-home')

4.2 User Views — users/[Link]


from [Link] import render, redirect
from [Link] import login_required
from [Link] import messages
from .forms import RegisterForm, UserUpdateForm, ProfileUpdateForm

def register(request):
if [Link] == 'POST':
form = RegisterForm([Link])
if form.is_valid():
[Link]()
[Link](request, 'Account created! You can now log in.')
return redirect('login')
else:
form = RegisterForm()
return render(request, 'users/[Link]', {'form': form})

@login_required
def profile(request):
if [Link] == 'POST':
u_form = UserUpdateForm([Link], instance=[Link])
p_form = ProfileUpdateForm([Link], [Link],
instance=[Link])
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
[Link](request, 'Profile updated!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=[Link])
p_form = ProfileUpdateForm(instance=[Link])
return render(request, 'users/[Link]', {'u_form': u_form, 'p_form':
p_form})
5. URL Configuration
5.1 Main URLs — myblog/[Link]
from [Link] import admin
from [Link] import path, include
from [Link] import views as auth_views
from [Link] import settings
from [Link] import static

urlpatterns = [
path('admin/', [Link]),
path('', include('[Link]')),
path('users/', include('[Link]')),
path('login/', auth_views.LoginView.as_view(template_name='users/[Link]'),
name='login'),
path('logout/',
auth_views.LogoutView.as_view(template_name='users/[Link]'), name='logout'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

5.2 Blog URLs — blog/[Link]


Create blog/[Link]:
from [Link] import path
from . import views

urlpatterns = [
path('', [Link], name='blog-home'),
path('post/new/', views.post_create, name='post-create'),
path('post/<int:pk>/edit/', views.post_edit, name='post-edit'),
path('post/<int:pk>/delete/', views.post_delete, name='post-delete'),
]

5.3 Users URLs — users/[Link]


Create users/[Link]:
from [Link] import path
from . import views

urlpatterns = [
path('register/', [Link], name='register'),
path('profile/', [Link], name='profile'),
]
6. Templates
6.1 Folder Structure
Create these folders first:
mkdir -p blog/templates/blog
mkdir -p users/templates/users
mkdir -p media/profile_pics

💡 Note: You also need a default profile image. Download any image and save it as
media/[Link]

6.2 Base Template — blog/templates/blog/[Link]


This is the master layout with Tailwind CSS via CDN + dark mode toggle:
<!DOCTYPE html>
<html lang='en' id='html-root'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>MyBlog</title>
<script src='[Link]
<script>
[Link] = { darkMode: 'class' }
</script>
</head>
<body class='bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-
screen transition-colors duration-300'>

<!-- NAVBAR -->


<nav class='bg-blue-700 dark:bg-blue-900 text-white px-6 py-3 flex items-center
justify-between shadow-lg'>
<a href='{% url "blog-home" %}' class='text-2xl font-bold tracking-
tight'>MyBlog</a>
<div class='flex items-center gap-4 text-sm'>
<a href='{% url "blog-home" %}' class='hover:underline'>Home</a>
{% if user.is_authenticated %}
<a href='{% url "post-create" %}' class='hover:underline'>New Post</a>
<a href='{% url "profile" %}' class='hover:underline'>Profile</a>
<a href='{% url "logout" %}' class='hover:underline'>Logout</a>
{% else %}
<a href='{% url "register" %}' class='hover:underline'>Register</a>
<a href='{% url "login" %}' class='hover:underline'>Login</a>
{% endif %}
<!-- Dark Mode Toggle -->
<button onclick='toggleDark()' class='ml-2 bg-white text-blue-700 dark:bg-gray-
700 dark:text-white px-3 py-1 rounded-full text-xs font-bold'>
🌙 Dark
</button>
</div>
</nav>

<!-- FLASH MESSAGES -->


<div class='max-w-4xl mx-auto mt-4 px-4'>
{% for message in messages %}
<div class='bg-green-100 border border-green-400 text-green-800 px-4 py-2
rounded mb-2'>
{{ message }}
</div>
{% endfor %}
</div>

<!-- MAIN CONTENT -->


<main class='max-w-4xl mx-auto px-4 py-8'>
{% block content %}{% endblock %}
</main>

<script>
function toggleDark() {
[Link]('dark');
[Link]('dark',
[Link]('dark'));
}
if ([Link]('dark') === 'true') {
[Link]('dark');
}
</script>
</body>
</html>

6.3 Home Page — blog/templates/blog/[Link]


{% extends 'blog/[Link]' %}
{% block content %}
<h1 class='text-3xl font-bold mb-6'>All Posts</h1>
{% for post in posts %}
<div class='bg-white dark:bg-gray-800 rounded-xl shadow p-6 mb-4'>
{% if [Link] %}
<img src='{{ [Link] }}' class='w-full h-48 object-cover rounded-lg
mb-4'>
{% endif %}
<h2 class='text-xl font-semibold'>{{ [Link] }}</h2>
<p class='text-sm text-gray-500 dark:text-gray-400 mb-2'>
By {{ [Link] }} &bull; {{ [Link]|date:'M d, Y' }}
</p>
<p class='text-gray-700 dark:text-gray-300'>{{ [Link]|
truncatewords:30 }}</p>
{% if user == [Link] %}
<div class='mt-3 flex gap-3'>
<a href='{% url "post-edit" [Link] %}' class='text-blue-500
hover:underline text-sm'>Edit</a>
<a href='{% url "post-delete" [Link] %}' class='text-red-500
hover:underline text-sm'
onclick='return confirm("Delete this post?")'>Delete</a>
</div>
{% endif %}
</div>
{% empty %}
<p class='text-gray-500'>No posts yet. <a href='{% url "post-create" %}'
class='text-blue-500 hover:underline'>Create the first one!</a></p>
{% endfor %}
{% endblock %}
6.4 Post Form — blog/templates/blog/post_form.html
{% extends 'blog/[Link]' %}
{% block content %}
<div class='bg-white dark:bg-gray-800 rounded-xl shadow p-8 max-w-2xl mx-auto'>
<h1 class='text-2xl font-bold mb-6'>{{ title }}</h1>
<form method='POST' enctype='multipart/form-data'>
{% csrf_token %}
{% for field in form %}
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>{{ [Link] }}</label>
{{ field }}
{% if [Link] %}
<p class='text-red-500 text-xs mt-1'>{{ [Link]|join:', ' }}</p>
{% endif %}
</div>
{% endfor %}
<button type='submit' class='bg-blue-600 hover:bg-blue-700 text-white px-6 py-2
rounded-lg font-semibold'>
Save Post
</button>
</form>
</div>
{% endblock %}

6.5 Register — users/templates/users/[Link]


{% extends 'blog/[Link]' %}
{% block content %}
<div class='bg-white dark:bg-gray-800 rounded-xl shadow p-8 max-w-md mx-auto'>
<h1 class='text-2xl font-bold mb-6'>Create Account</h1>
<form method='POST'>
{% csrf_token %}
{% for field in form %}
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>{{ [Link] }}</label>
{{ field }}
{% if [Link] %}
<p class='text-red-500 text-xs mt-1'>{{ [Link]|join:', ' }}</p>
{% endif %}
{% if field.help_text %}
<p class='text-gray-400 text-xs mt-1'>{{ field.help_text }}</p>
{% endif %}
</div>
{% endfor %}
<button type='submit' class='w-full bg-blue-600 hover:bg-blue-700 text-white
py-2 rounded-lg font-semibold'>
Register
</button>
</form>
<p class='mt-4 text-sm text-center'>Already have an account? <a href='{% url
"login" %}' class='text-blue-500 hover:underline'>Login</a></p>
</div>
{% endblock %}

6.6 Login — users/templates/users/[Link]


{% extends 'blog/[Link]' %}
{% block content %}
<div class='bg-white dark:bg-gray-800 rounded-xl shadow p-8 max-w-md mx-auto'>
<h1 class='text-2xl font-bold mb-6'>Login</h1>
<form method='POST'>
{% csrf_token %}
{% for field in form %}
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>{{ [Link] }}</label>
{{ field }}
{% if [Link] %}
<p class='text-red-500 text-xs mt-1'>{{ [Link]|join:', ' }}</p>
{% endif %}
</div>
{% endfor %}
<button type='submit' class='w-full bg-blue-600 hover:bg-blue-700 text-white
py-2 rounded-lg font-semibold'>
Login
</button>
</form>
<p class='mt-4 text-sm text-center'>No account? <a href='{% url "register" %}'
class='text-blue-500 hover:underline'>Register</a></p>
</div>
{% endblock %}

6.7 Logout — users/templates/users/[Link]


{% extends 'blog/[Link]' %}
{% block content %}
<div class='text-center mt-20'>
<h1 class='text-3xl font-bold mb-4'>You have been logged out.</h1>
<a href='{% url "login" %}' class='text-blue-500 hover:underline text-lg'>Login
again</a>
</div>
{% endblock %}

6.8 Profile — users/templates/users/[Link]


{% extends 'blog/[Link]' %}
{% block content %}
<div class='bg-white dark:bg-gray-800 rounded-xl shadow p-8 max-w-lg mx-auto'>
<div class='flex items-center gap-6 mb-6'>
<img src='{{ [Link] }}' class='w-24 h-24 rounded-full object-
cover border-4 border-blue-400'>
<div>
<h1 class='text-2xl font-bold'>{{ [Link] }}</h1>
<p class='text-gray-500'>{{ [Link] }}</p>
</div>
</div>
<form method='POST' enctype='multipart/form-data'>
{% csrf_token %}
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>Username</label>
{{ u_form.username }}
</div>
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>Email</label>
{{ u_form.email }}
</div>
<div class='mb-4'>
<label class='block font-semibold mb-1 text-sm'>Profile Picture</label>
{{ p_form.image }}
</div>
<button type='submit' class='bg-blue-600 hover:bg-blue-700 text-white px-6 py-2
rounded-lg font-semibold'>
Update Profile
</button>
</form>
</div>
{% endblock %}

7. Style Form Inputs with Tailwind


Django renders form inputs as plain HTML. To apply Tailwind classes to them, add this to both
[Link] files. Edit blog/[Link]:
from django import forms
from .models import Post

INPUT_CLASS = 'w-full border border-gray-300 dark:border-gray-600 rounded-lg px-3


py-2 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none
focus:ring-2 focus:ring-blue-500'

class PostForm([Link]):
class Meta:
model = Post
fields = ['title', 'content', 'image']
widgets = {
'title': [Link](attrs={'class': INPUT_CLASS}),
'content': [Link](attrs={'class': INPUT_CLASS, 'rows': 6}),
}

Edit users/[Link] — add the same INPUT_CLASS and widgets to RegisterForm,


UserUpdateForm, and ProfileUpdateForm:
INPUT_CLASS = 'w-full border border-gray-300 dark:border-gray-600 rounded-lg px-3
py-2 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:outline-none
focus:ring-2 focus:ring-blue-500'

class RegisterForm(UserCreationForm):
email = [Link](widget=[Link](attrs={'class': INPUT_CLASS}))
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
widgets = {
'username': [Link](attrs={'class': INPUT_CLASS}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
[Link]['password1'].[Link]({'class': INPUT_CLASS})
[Link]['password2'].[Link]({'class': INPUT_CLASS})

class UserUpdateForm([Link]):
email = [Link](widget=[Link](attrs={'class': INPUT_CLASS}))
class Meta:
model = User
fields = ['username', 'email']
widgets = { 'username': [Link](attrs={'class': INPUT_CLASS}) }

class ProfileUpdateForm([Link]):
class Meta:
model = Profile
fields = ['image']

8. Create Superuser & Run


# Create an admin account
python3 [Link] createsuperuser

# Start the development server


python3 [Link] runserver

Open your browser and go to: [Link]

Admin panel: [Link]

9. Final File Structure


Your project should look like this:
myblog/
├── [Link]
├── myblog/
│ ├── [Link]
│ └── [Link]
├── blog/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ └── templates/blog/
│ ├── [Link]
│ ├── [Link]
│ └── post_form.html
├── users/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ └── templates/users/
│ ├── [Link]
│ ├── [Link]
│ ├── [Link]
│ └── [Link]
└── media/
└── [Link]

10. Push to GitHub


Step 1: Create a new repository on [Link] (no README, no .gitignore)
Step 2: In your project folder, run:
# Create .gitignore first
echo '__pycache__/\n*.pyc\nmedia/\ndb.sqlite3\[Link]' > .gitignore

git init
git add .
git commit -m 'Initial Django blog project'
git branch -M main
git remote add origin [Link]
git push -u origin main

💡 Note: Replace YOUR_USERNAME and YOUR_REPO with your actual GitHub username and
repo name.

11. Common Errors & Fixes


'No module named Pillow'
pip3 install Pillow

Profile has no image / RelatedObjectDoesNotExist


This means the signals didn't fire for an existing user. Fix by running:
python3 [Link] shell
>>> from [Link] import User
>>> from [Link] import Profile
>>> for u in [Link]():
... [Link].get_or_create(user=u)
>>> exit()

TemplateDoesNotExist
Double-check your folder path. Django is case-sensitive. Verify: blog/templates/blog/[Link]
(not Blog or BLOG).

CSRF verification failed


Always include {% csrf_token %} inside every <form> tag.
Good luck with your project!

You might also like