Blogging app

HOW TO


Your First Blogging app

Create a django project and create an named home with following commands

Mkdir blogapp && cd blogapp
Python3 -m venv .venv
# In windows,
.venv/Scripts/activate or .venv\scripts\activate
# In MacOS/linux,
source .venv/bin/activate
pip install django django-tinymce pillow
django-admin startproject blogproject .
django-admin startapp home

Open settings.py and add ‘home’ & ‘tinymce’ to installed_apps list In blogproject/urls.py add this line

"""1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path  
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,include #import include
    
urlpatterns = [  
    path('admin/', admin.site.urls),
    path('',include('home.urls')) #add this line
]

Create a urls.py file inside home folder

from django.urls import path
from .views import home,detail
urlpatterns  = [
        path('',home,name="home"),
        path('blog/<int:id>/',detail,name="detail"),
]

Goto models.py and create a class named Blog:

from django.db import models
from django.contrib.auth.models import User
from tinymce.models import HTMLField
from django.urls import reverse
class Blog(models.Model):
	image = models.ImageField(upload_to="blog_images/")
	title= models.CharField(max_length=30)
	description = HTMLField()
	author = models.ForeignKey(User,on_delete=models.CASCADE)
	def __str__(self):
		return f"{self.title} - {self.author}"
	def get_absolute_url(self):
		return reverse('detail',args=[self.id])

Goto admin.py and create a BlogAdmin to assign the request user as the admin & hide the author field in admin panel.

from django.contrib import admin
from .models import Blog
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
    exclude = ('author',)
    def save_model(self, request, obj, form, change):
        if not obj.pk:
            obj.author = request.user
        super().save_model(request, obj, form, change)

Now lets setup Staticfiles like CSS,JS and HTML. And also media files (User uploaded static files)

Goto settings.py and add these new configs along with static_url

STATIC_URL = 'static/'
STATIC_ROOT = 'staticfiles/'
STATICFILES_DIRS = [ BASE_DIR / 'static',]
        
MEDIA_URL = '/media/'  
MEDIA_ROOT = BASE_DIR / 'media'

Goto blogproject/urls.py and make it

from django.contrib import admin
from django.urls import path,include
from django.conf import settings #import settings 
from django.conf.urls.static import static #import static function
urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('home.urls'))
]
if settings.DEBUG:
	#handle static files
	urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT) 
	#handle media files
	urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

Goto views.py

from django.shortcuts import render,get_object_or_404
from .models import Blog
from django.core import paginator
# Create your views here.
def home(req):
        blogs = Blog.objects.all()
        blogs = paginator.Paginator(blogs,9)
        page_number = req.GET.get('page')
        blogs = blogs.get_page(page_number)
        return render(req,'index.html',{'blogs':blogs})
def detail(req,id):
        Blog = get_object_or_404(Blog,id=id)
        return render(req,detail.html',{'blog':blog})

Create templates folder and index.html inside it.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Blog — Home</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">

    <style>
        /* Small custom styles */
        .hero {
            background: linear-gradient(90deg, #0d6efd22 0%, #6c757d11 100%);
        }
        .post-excerpt { color: #6c757d; }
        .tag { font-size: .85rem; margin: .15rem; }
        .card-img-top { object-fit: cover; height: 180px; }
    </style>
</head>
<body>

    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom">
        <div class="container">
            <a class="navbar-brand fw-bold" href="#">MyBlog</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navMenu">
                <ul class="navbar-nav ms-auto align-items-lg-center">
                    <li class="nav-item"><a class="nav-link" href="#">Home</a></li>
                    <li class="nav-item"><a class="nav-link" href="#">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <!-- Hero -->
    <header class="hero py-5 mb-4">
        <div class="container text-center">
            <h1 class="display-5 fw-bold">Thoughts, Tutorials & Stories</h1>
            <p class="lead text-muted mb-3">A clean, responsive Bootstrap blog layout to showcase your articles.</p>
            <div>
                <a class="btn btn-primary me-2" href="#">Get Started</a>
                <a class="btn btn-outline-secondary" href="#">Browse Categories</a>
            </div>
        </div>
    </header>

    <!-- Main content -->
    <main class="container mb-5">
        <div class="row g-4">
            <!-- Posts -->
            <div class="col-lg-8">
                <!-- Post Card -->
                {% for blog in blogs %}
                <article class="card mb-4 shadow-sm">
                    <img src="{{ blog.image.url }}" class="card-img-top" alt="Post image">
                    <div class="card-body">
                        <h3 class="card-title">{{ blog.title }}</h3>
                        <p class="text-muted small mb-1">By {{ blog.author }}</p>
                        <p class="post-excerpt mb-3">{{ blog.description|truncatechars_html:'70'|safe }}</p>
                        <a class="btn btn-sm btn-primary" href="{% url "detail" blog.id %}">Read more</a>
                    </div>
                </article>
                {% endfor %}
                

                <!-- Pagination -->
                <nav aria-label="Page navigation">
                    <ul class="pagination justify-content-center">
                        {% if blogs.has_previous %}
                        <li class="page-item">
                            <a class="page-link" href="?page={{ blogs.previous_page_number }}" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </a>
                        </li>
                        {% else %}
                        <li class="page-item disabled">
                            <span class="page-link" aria-label="Previous">
                                <span aria-hidden="true">&laquo;</span>
                            </span>
                        </li>
                        {% endif %}

                        {% for num in blogs.paginator.page_range %}
                            {% if blogs.number == num %}
                                <li class="page-item active"><span class="page-link">{{ num }}</span></li>
                            {% else %}
                                <li class="page-item"><a class="page-link" href="?page={{ num }}">{{ num }}</a></li>
                            {% endif %}
                        {% endfor %}

                        {% if blogs.has_next %}
                        <li class="page-item">
                            <a class="page-link" href="?page={{ blogs.next_page_number }}" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </a>
                        </li>
                        {% else %}
                        <li class="page-item disabled">
                            <span class="page-link" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                            </span>
                        </li>
                        {% endif %}
                </nav>
            </div>

            <!-- Sidebar -->
            <aside class="col-lg-4">
                <!-- Search -->
                <div class="card mb-4 shadow-sm">
                    <div class="card-body">
                        <form class="d-flex" role="search" action="#" method="get">
                            <input class="form-control me-2" type="search" placeholder="Search posts" aria-label="Search">
                            <button class="btn btn-primary" type="submit">Search</button>
                        </form>
                    </div>
                </div>

                <!-- About -->
                <div class="card mb-4 shadow-sm">
                    <div class="card-body">
                        <h6 class="card-title">About the author</h6>
                        <p class="card-text text-muted">Short bio or description that introduces the blog and the author. Link to about page for more.</p>
                        <a href="#" class="btn btn-sm btn-outline-primary">Learn more</a>
                    </div>
                </div>

                <!-- Categories -->
                <div class="card mb-4 shadow-sm">
                    <div class="card-body">
                        <h6 class="card-title">Categories</h6>
                        <ul class="list-unstyled mb-0">
                            <li><a href="#" class="text-decoration-none">Design</a> <span class="text-muted small">· 12</span></li>
                            <li><a href="#" class="text-decoration-none">Development</a> <span class="text-muted small">· 24</span></li>
                            <li><a href="#" class="text-decoration-none">Tutorials</a> <span class="text-muted small">· 9</span></li>
                            <li><a href="#" class="text-decoration-none">Opinion</a> <span class="text-muted small">· 6</span></li>
                        </ul>
                    </div>
                </div>

                <!-- Tags -->
                <div class="card mb-4 shadow-sm">
                    <div class="card-body">
                        <h6 class="card-title">Tags</h6>
                        <div>
                            <a href="#" class="btn btn-outline-secondary btn-sm tag">bootstrap</a>
                            <a href="#" class="btn btn-outline-secondary btn-sm tag">css</a>
                            <a href="#" class="btn btn-outline-secondary btn-sm tag">javascript</a>
                            <a href="#" class="btn btn-outline-secondary btn-sm tag">html</a>
                            <a href="#" class="btn btn-outline-secondary btn-sm tag">webdev</a>
                        </div>
                    </div>
                </div>

                <!-- Recent posts -->
                <div class="card shadow-sm">
                    <div class="card-body">
                        <h6 class="card-title">Recent posts</h6>
                        <ul class="list-unstyled">
                            <li class="mb-2">
                                <a class="text-decoration-none" href="#">Design systems for small teams</a>
                                <div class="text-muted small">May 10, 2025</div>
                            </li>
                            <li class="mb-2">
                                <a class="text-decoration-none" href="#">Optimizing images for fast delivery</a>
                                <div class="text-muted small">Apr 26, 2025</div>
                            </li>
                            <li>
                                <a class="text-decoration-none" href="#">Accessibility basics</a>
                                <div class="text-muted small">Mar 08, 2025</div>
                            </li>
                        </ul>
                    </div>
                </div>

            </aside>
        </div>
    </main>

    <!-- Footer -->
    <footer class="bg-light py-4 border-top">
        <div class="container text-center small text-muted">
            © 2025 MyBlog · Built with Bootstrap
        </div>
    </footer>

    <!-- Bootstrap JS bundle (includes Popper) -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Detail.html

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>{{ blog.title }} — Post</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">

    <style>
        .card-img-top { object-fit: cover; height: 360px; }
        .post-meta { color: #6c757d; font-size: .95rem; }
        .post-body { white-space: pre-wrap; }
    </style>
</head>
<body>

    <nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom">
        <div class="container">
            <a class="navbar-brand fw-bold" href="{% url 'home' %}">MyBlog</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navMenu">
                <span class="navbar-toggler-icon"></span>
            </button>

            <div class="collapse navbar-collapse" id="navMenu">
                <ul class="navbar-nav ms-auto">
                    <li class="nav-item"><a class="nav-link" href="{% url 'home' %}">Home</a></li>
                    <li class="nav-item"><a class="nav-link" href="#">Contact</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <main class="container my-5">
        <article class="card shadow-sm">
            {% if blog.image %}
            <img src="{{ blog.image.url }}" class="card-img-top" alt="{{ blog.title }}">
            {% endif %}

            <div class="card-body">
                <h1 class="card-title display-6 mb-2">{{ blog.title }}</h1>
                <div class="d-flex align-items-center mb-3">
                    <div class="me-3">
                        {% if blog.author.profile.image %}
                            <img src="{{ blog.author.profile.image.url }}" alt="{{ blog.author }}" class="rounded-circle" style="width:48px;height:48px;object-fit:cover;">
                        {% endif %}
                    </div>
                    <div>
                        <div class="post-meta">By <strong>{{ blog.author }}</strong></div>
                        {% if blog.published_date %}
                        <div class="post-meta small">{{ blog.published_date|date:"F j, Y" }}</div>
                        {% endif %}
                    </div>
                </div>

                <div class="post-body mb-4">
                    {{ blog.description|safe }}
                </div>

                <a href="{% url 'home' %}" class="btn btn-outline-secondary">← Back to posts</a>
            </div>
        </article>
    </main>

    <footer class="bg-light py-4 border-top">
        <div class="container text-center small text-muted">
            © {{ now.year }} MyBlog · Built with Bootstrap
        </div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>