Blogging Platform using Django - GeeksforGeeks

Blogging Platform using Django

Last Updated : 13 Nov, 2026

Blogging Platform is a Django-based web application that allows users to create, view, edit, and delete blog posts with images. It uses Django models to store blog information, views to handle user requests, and templates to display posts dynamically.

  • New blog posts can be added through the Django Admin Panel, while the website provides options to view, edit, and delete existing posts.
  • Users can create blog posts with titles, descriptions, and images through the Django Admin Panel.
  • Application displays all published blog posts with their images and short descriptions.
  • Users can open individual blog posts to view their complete descriptions.
  • Existing blog posts can be edited or deleted through the website.
  • Django models store blog details, including creation and update timestamps.
  • Uploaded images are stored in the media directory and displayed dynamically using Django templates.
  • Bootstrap is used to create a responsive layout for displaying blog posts.

1. Create Django Project

Start by creating a Django project named blogsite and an app named gallery.

django-admin startproject blogsite
cd blogsite
python manage.py startapp gallery

Explanation:

  • startproject creates a new Django project named blogsite.
  • cd blogsite navigates into the project directory.
  • startapp creates a Django application named gallery.

Add gallery to the INSTALLED_APPS list in blogsite/settings.py.

Python
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'gallery',
]

Explanation:

  • INSTALLED_APPS contains all Django applications enabled in the project.
  • Adding gallery registers the application with the Django project.

2. Creating Product Model

Product model represents a blog post and stores its title, description, image, and timestamps.

Open gallery/models.py and add the following code:

Python
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=255)
    description = models.TextField()
    image = models.ImageField(upload_to='products/')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    def edit(self, name, description, image):
        self.name = name
        self.description = description
        self.image = image
        self.save()


    def short_description(self):
        # Split the description into words
        words = self.description.split()
        if len(words) > 50:
            # Join the first 50 words and add "..." at the end
            return ' '.join(words[:30]) + '...'
        else:
            # If the description is already less than 50 words, return it as is
            return self.description

Explanation:

  • name stores the title of the blog post.
  • description stores the complete blog content.
  • image stores the uploaded image in the products/ directory inside the media folder.
  • created_at automatically records when a blog post is created.
  • updated_at automatically records when a blog post is modified.
  • __str__() returns the blog title as the readable representation of the object.
  • short_description() returns the first 50 words of the description for displaying blog previews.

3. Registering Model in Django Admin

Register the Product model in the Django Admin Panel so that administrators can create and manage blog posts.

Open gallery/admin.py and add:

Python
from django.contrib import admin
from .models import Product

admin.site.register(Product)

Explanation:

  • admin.site.register(Product) registers the Product model with the Django Admin Panel.
  • Administrators can add, view, update, and delete blog posts through the admin interface.

4. Creating Model Form

Create gallery/forms.py to define a Django ModelForm for editing existing blog posts.

Python
from django import forms
from .models import Product

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'description', 'image']

Explanation:

  • ModelForm automatically generates form fields using the Product model.
  • Model attribute specifies which model the form is associated with.
  • Fields attribute specifies the model fields that can be edited.
  • Django handles form validation and saving the updated information.

5. Defining Views

Create views in gallery/views.py to display, edit, and delete blog posts.

Python
from django.shortcuts import render, redirect, get_object_or_404
from .models import Product
from .forms import ProductForm


def product_list(request):
    products = Product.objects.all()
    return render(request, 'gallery/index.html', {'products': products})


def product_detail(request, pk):
    product = get_object_or_404(Product, pk=pk)
    return render(request, 'gallery/index2.html', {'product': product})


def edit_product(request, pk):
    product = get_object_or_404(Product, pk=pk)

    if request.method == 'POST':
        form = ProductForm(request.POST, request.FILES, instance=product)

        if form.is_valid():
            form.save()
            return redirect('product_list')

    else:
        form = ProductForm(instance=product)

    return render(request, 'gallery/edit.html', {'form': form})


def delete_product(request, pk):
    product = get_object_or_404(Product, pk=pk)

    if request.method == 'POST':
        product.delete()
        return redirect('product_list')

    return render(request, 'gallery/delete.html', {'product': product})

Explanation:

  • product_list() retrieves all blog posts from the database and displays them on the homepage.
  • product_detail() retrieves an individual blog post using its primary key and displays its complete details.
  • edit_product() loads the existing post into a form and saves the changes after validation.
  • delete_product() deletes the selected blog post when the user submits the confirmation form.
  • get_object_or_404() retrieves an object or returns a 404 error if it does not exist.
  • redirect() sends the user back to the blog homepage after editing or deleting a post.
  • request.FILES handles image uploads when editing a blog post.

6. Configuring URLs

Creating gallery/urls.py to define URL patterns for displaying, editing, and deleting blog posts.

Python
from django.urls import path
from . import views

urlpatterns = [
    path('', views.product_list, name='product_list'),
    path('<int:pk>/', views.product_detail, name='product_detail'),
    path('<int:pk>/edit/', views.edit_product, name='edit_product'),
    path('<int:pk>/delete/', views.delete_product, name='delete_product'),
]

Explanation:

  • Empty path maps the homepage to product_list().
  • <int:pk>/ displays an individual blog post using its primary key.
  • <int:pk>/edit/ opens the editing form for the selected post.
  • <int:pk>/delete/ opens the deletion confirmation page.
  • Name attribute provides a name for each URL pattern that can be referenced in Django templates.

Now open blogsite/urls.py and include the gallery app URLs.

Python
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('', include('gallery.urls')),
]

if settings.DEBUG:
    urlpatterns += static(
        settings.MEDIA_URL,
        document_root=settings.MEDIA_ROOT
    )

Explanation:

  • admin.site.urls enables the Django Admin Panel.
  • include('gallery.urls') connects the project's URL configuration with the gallery app.
  • static() serves uploaded media files during local development.
  • DEBUG condition ensures that this media-serving configuration is used only during development.

6. Configuring Media Settings

Django requires media settings to manage uploaded images.

Open blogsite/settings.py and add:

Python
import os

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Explanation:

  • MEDIA_URL defines the URL prefix used to access uploaded media files.
  • MEDIA_ROOT specifies the filesystem directory where uploaded images are stored.
  • Uploaded blog images are saved inside the media/products/ directory.

7. Creating HTML Templates

Create the following directory structure inside the gallery app:

gallery/
templates/
gallery/
index.html
index2.html
edit.html
delete.html

Each template handles a specific operation of the blogging platform.

1. edit.html : Template displays a form for updating an existing blog post.

Python
<h2>Edit Blog Post</h2>

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save Changes</button>
</form>

Explanation:

  • POST method submits the updated blog information.
  • enctype="multipart/form-data" allows users to upload image files.
  • {% csrf_token %} protects the form against CSRF attacks.
  • {{ form.as_p }} displays the form fields as HTML paragraphs.
  • Submit button sends the updated information to the Django view.

2. delete.html : This template displays a confirmation message before deleting a blog post.

Python
<h2>Delete Blog Post</h2>

<p>Are you sure you want to delete "{{ product.name }}"?</p>

<form method="post">
    {% csrf_token %}
    <button type="submit">Yes, Delete</button>
</form>

Explanation:

  • {{ product.name }} displays the title of the selected blog post.
  • {% csrf_token %} protects the deletion form against CSRF attacks.
  • POST request confirms the deletion operation.
  • After deletion, the user is redirected to the blog homepage.

3. index.html : This template displays all blog posts on the homepage using Bootstrap cards.

Python
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog Website</title>

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>
<body>

    <div class="container mt-5">
        <h1>Today's New Blogs</h1>

        <div class="row">
            {% for product in products %}
                <div class="col-md-4 mb-4">
                    <div class="card">

                        <a href="{% url 'product_detail' product.pk %}">
                            <img src="{{ product.image.url }}"
                                 alt="{{ product.name }}"
                                 class="card-img-top">
                        </a>

                        <div class="card-body">
                            <h5 class="card-title">{{ product.name }}</h5>

                            <p class="card-text">
                                {{ product.short_description }}
                            </p>

                            <a href="{% url 'edit_product' product.pk %}">Edit</a>

                            <a href="{% url 'delete_product' product.pk %}">Delete</a>

                            <br>

                            <small>Created at: {{ product.created_at }}</small>
                            <br>

                            <small>Updated at: {{ product.updated_at }}</small>
                        </div>

                    </div>
                </div>
            {% endfor %}
        </div>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>

</body>
</html>

Explanation:

  • {% for product in products %} iterates through all blog posts retrieved from the database.
  • Bootstrap cards organize each blog post into a separate container.
  • {{ product.image.url }} displays the uploaded blog image.
  • {{ product.name }} displays the blog title.
  • {{ product.short_description }} displays a shortened version of the blog content.
  • Edit and Delete links navigate to their respective operations.
  • created_at and updated_at display the timestamps of each blog post.
  • Bootstrap CSS and JavaScript files provide styling and interface functionality.

4. index2.html : This template displays the complete details of an individual blog post.

Python
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{{ product.name }} - Blog Detail</title>
</head>
<body>

    <h1>{{ product.name }} - Blog</h1>

    <div>
        <img src="{{ product.image.url }}"
             alt="{{ product.name }}"
             width="200">
    </div>

    <h2>{{ product.name }}</h2>

    <p>{{ product.description }}</p>

    <a href="{% url 'edit_product' product.pk %}">Edit</a>

    <a href="{% url 'delete_product' product.pk %}">Delete</a>

    <br>

    <a href="{% url 'product_list' %}">Back to Blogs Home Page</a>

</body>
</html>

Explanation:

  • {{ product.name }} displays the title of the selected blog post.
  • {{ product.image.url }} displays its uploaded image.
  • {{ product.description }} displays the complete blog content.
  • Edit and Delete links allow users to modify or remove the selected post.
  • Back to Blogs Home Page link redirects users to the homepage.

8. Installing Required Packages

Make sure to install Pillow for image handling:

pip install Pillow

9. Database Setup and Superuser Creation

Apply Migrations

python manage.py makemigrations
python manage.py migrate

Create a superuser to access the Django Admin Panel.

python manage.py createsuperuser

You will be prompted to enter a username, email, and password.

8. Running Project

Start the Django development server using:

python manage.py runserver

Log in using the superuser credentials and add blog entries with images through the Product model.

Screenshot-from-2023-10-01-14-26-49

Output:

Screenshot-from-2023-10-02-17-09-25-(1)

Screenshot-from-2023-10-02-17-10-22

Screenshot-from-2023-10-02-17-11-44
Screenshot-from-2023-10-02-17-10-44

Comment