Install django-summernote.
pip install django-summernote
django_summernote to INSTALLED_APP in settings.py.
INSTALLED_APPS += ('django_summernote', )
django_summernote.urls to urls.py.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('summernote/', include('django_summernote.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Media settings
X_FRAME_OPTIONS = 'SAMEORIGIN'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
To explore more configuration read the official documentation of the package, https://github.com/summernote/django-summernote
//blogsite/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'myapp', #add myapp ] INSTALLED_APPS += ('django_summernote', ) X_FRAME_OPTIONS = 'SAMEORIGIN'myapp/views.py
//myapp/views.py from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login, logout from django.contrib.auth.models import User from django.contrib import messages from myapp.models import Category, Post category_list = Category.objects.exclude(status = 2).all() context = { 'page_title' : 'Simple Blog Site', 'category_list' : category_list, 'category_list_limited' : category_list[:3] } # Create your views here. #Logout def logoutuser(request): logout(request) return redirect('/') def home(request): context['page_title'] = 'Home' posts = Post.objects.filter(status = 1).all() context['posts'] = posts return render(request, 'home.html',context) def view_post(request,pk=None): context['page_title'] = "" if pk is None: messages.error(request,"Unabale to view Post") return redirect('home-page') else: post = Post.objects.filter(id = pk).first() context['page_title'] = post.title context['post'] = post return render(request, 'view_post.html',context) def post_by_category(request,pk=None): if pk is None: messages.error(request,"Unabale to view Post") return redirect('home-page') else: category = Category.objects.filter(id=pk).first() context['page_title'] = category.name context['category'] = category posts = Post.objects.filter(category = category).all() context['posts'] = posts return render(request, 'by_categories.html',context) def categories(request): categories = Category.objects.filter(status = 1).all() context['page_title'] = "Category Management" context['categories'] = categories return render(request, 'categories.html',context)blogsite/urls.py
//blogsite/urls.py from django.contrib import admin from django.urls import include, path from django.conf import settings from django.conf.urls.static import static urlpatterns = [ path('admin/', admin.site.urls), path('', include('myapp.urls')), path('summernote/', include('django_summernote.urls')), ] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)myspp/urls.py
//myspp/urls.py from . import views from django.contrib import admin from django.urls import path, re_path from django.contrib.auth import views as auth_views from django.views.generic.base import RedirectView urlpatterns = [ path('redirect-admin', RedirectView.as_view(url="/admin"),name="redirect-admin"), path('', views.home, name="home-page"), path('logout',views.logoutuser,name='logout'), path(r'view_post/<int:pk>',views.view_post,name='view-post'), path(r'<int:pk>',views.post_by_category,name='category-post'), path('categories',views.categories,name='category-page'), ]Make Migrations
Run the commands below to make migrations:
python manage.py makemigrations
python manage.py migrate
C:\django\blogsite>python manage.py makemigrations
C:\django\blogsite>python manage.py migrate
myapp/models.py
//myapp/models.py from django.db import models from unicodedata import category from django.db import models from django.contrib.auth.models import User from django.utils import timezone class Category(models.Model): name = models.CharField(max_length=250) description = models.TextField(blank=True, null=True) status = models.IntegerField(default = 1) date_added = models.DateTimeField(default=timezone.now) date_updated = models.DateTimeField(auto_now=True) def __str__(self): return self.name class Post(models.Model): category=models.ForeignKey(Category,on_delete=models.CASCADE) title = models.TextField() author = models.ForeignKey(User,on_delete=models.CASCADE) blog_post = models.TextField() banner = models.ImageField(blank=True, null = True, upload_to= 'images/') status = models.IntegerField(default = 0) date_added = models.DateTimeField(default=timezone.now) date_updated = models.DateTimeField(auto_now=True) def __str__(self): return self.title + " - " + self.category.nameBootstrap 5
https://getbootstrap.com/docs/5.0/getting-started/introduction/
https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css
myapp/templates/home.html
//myapp/templates/home.html {% extends "base.html" %} {% block pageContent %} <section class="text-center"> <h4 class="mb-5"><strong>All Posts</strong></h4> <div class="row"> {% for post in posts %} <div class="col-lg-4 col-md-6 mb-4"> <div class="card shadow border"> <div class="bg-image hover-overlay ripple" data-mdb-ripple-color="light"> <img src="{% if post.banner %}{{ post.banner.url }}{% else %}{{ MEDIA_URL}}/media/default/python-django.png{% endif %}" class="img-fluid post-banner bg-gradient bg-dark" /> <a href="#!"> <div class="mask" style="background-color: rgba(251, 251, 251, 0.15);"></div> </a> </div> <div class="card-body"> <h5 class="card-title">{{ post.title }}</h5> <div class="card-text truncate-3">{{ post.blog_post|safe }} </div> <a href="{% url 'view-post' post.id %}" class="btn btn-primary">Read</a> </div> </div> </div> {% endfor %} </div> {% if not posts %} <center>No Bogs has been posted yet</center> {% endif %} </section> </div> {% endblock pageContent %}myapp/templates/base.html
//myapp/templates/base.html {% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {% if page_title %} <title>{{ page_title }} | Blog Site</title> {% else %} <title>Blog Site</title> {% endif %} <link rel="stylesheet" href="{% static 'blogApp/assets/bootstrap/css/bootstrap.min.css' %}"> </head> <body> {% block TopNavigation %} {% include "TopNavigation.html" %} {% endblock TopNavigation %} <main class="my-5"> <div class="container" style="padding-top: 50px;"> {% block pageContent %} {% endblock pageContent %} </div> </main> {% block ScriptBlock %} {% endblock ScriptBlock %} <footer class="bg-light text-lg-start"> <div class="text-center p-3" style="background-color: rgba(0, 0, 0, 0.2);"> © {% now 'Y' %} Copyright: <a class="text-dark" href="#" target="_blank">Cairocoders</a> </div> </footer> </body> </html>myapp/templates/TopNavigation.html
//myapp/templates/TopNavigation.html {% load static %} <header> <nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top"> <div class="container"> <a class="navbar-brand" target="_blank" href="#">Cairocoders</a> <button class="navbar-toggler" type="button" data-mdb-toggle="collapse" data-mdb-target="#navbarExample01" aria-controls="navbarExample01" aria-expanded="false" aria-label="Toggle navigation"> <i class="fas fa-bars"></i> </button> <div class="collapse navbar-collapse" id="navbarExample01"> <ul class="navbar-nav me-auto mb-2 mb-lg-0"> <li class="nav-item active"> <a class="nav-link" aria-current="page" href="{% url 'home-page' %}">Home</a> </li> {% for category in category_list_limited %} <li class="nav-item"> <a class="nav-link" href="{% url 'category-post' category.pk %}">{{ category.name }}</a> </li> {% endfor %} <li class="nav-item"> <a class="nav-link" href="{% url 'category-page' %}">All Categories</a> </li> </ul> <ul class="navbar-nav d-flex flex-row"> {% if user.id %} <li class="nav-item me-3 me-lg-0"> Hello, {{ user.first_name }} {{user.last_name}} </li> <li class="nav-item me-3 me-lg-0"> <a class="nav-link" href="{% url 'logout' %}">Logout</a> </li> {% endif %} </ul> </div> </div> </nav> </header>myapp/templates/view_post.html
//myapp/templates/view_post.html {% extends "base.html" %} {% block pageContent %} <section class="text-center"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="row justify-content-center"> <div class=" col-lg-12 col-md-12 col-sm-12 col-xs-12 card card-default rounded-0 shadow"> <div class="card-body"> <center> <img src="{% if post.banner %}{{ post.banner.url }}{% else %}{{ MEDIA_URL}}/media/default/python-django.png{% endif %}" alt="" class="img-fluid bg-gradient" id="view-post-banner"> </center> <h4 class="fw-bolder mt-4 text-start">{{ post.title }}</h4> <hr> <div class="lh-1 text-start"> <span class="me-5"><small>Author: <b>{{ post.author }}</b></small></span> <span class="me-5"><small>Category: <b>{{ post.category }}</b></small></span> <span><small><i class="fa fa-calendar-day"></i> Published: <b>{{ post.date_added|date:"F d, Y h:i A" }}</b></small></span> </div> <div class="clear-fix py-3"></div> <div>{{ post.blog_post|safe }}</div> </div> </div> </div> </div> </section> </div> {% endblock pageContent %}myapp/templates/by_categories.html
//myapp/templates/by_categories.html {% extends "base.html" %} {% block pageContent %} <section class="text-center"> <h4 class="mb-5"><strong>'{{ category }}' Posts</strong></h4> <div class="row"> {% for post in posts %} <div class="col-lg-4 col-md-6 mb-4"> <div class="card shadow border"> <div class="bg-image hover-overlay ripple" data-mdb-ripple-color="light"> <img src="{% if post.banner %}{{ post.banner.url }}{% else %}{{ MEDIA_URL}}/media/default/python-django.png{% endif %}" class="img-fluid post-banner bg-gradient bg-dark" /> <a href="#!"> <div class="mask" style="background-color: rgba(251, 251, 251, 0.15);"></div> </a> </div> <div class="card-body"> <h5 class="card-title">{{ post.title }}</h5> <div class="card-text truncate-3">{{ post.blog_post|safe }} </div> <a href="{% url 'view-post' post.id %}" class="btn btn-primary">Read</a> </div> </div> </div> {% endfor %} </div> {% if not posts %} <center>No Bogs has been posted yet</center> {% endif %} </section> </div> {% endblock pageContent %}myapp/templates/categories.html
//myapp/templates/categories.html {% extends 'base.html' %} {% block pageContent %} <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="card card-default rounded-0 shadow "> <div class="card-header"> <div class="d-flex w-100 align-items-center justify-content-between"> <h4 class="card-title fw-bold">Category Management</h4> </div> </div> <div class="card-body"> <div class="container-fluid"> <div id="list" class="list-group"> {% for category in categories %} <a href="{% url 'category-post' category.id %}" class="list-group-item list-group-item-action"> <h4><b>{{ category.name }}</b></h4> <hr> <p>{{ category.description }}</p> </a> {% endfor %} </div> {% if not categories %} <center>No Category Listed Yet</center> {% endif %} </div> </div> </div> </div> {% endblock pageContent %}myapp/admin.py
//myapp/admin.py import django from django.contrib import admin from myapp.models import Category, Post from django_summernote.admin import SummernoteModelAdmin # Register your models here. admin.site.register(Category) #admin.site.register(Post) class PostAdmin(SummernoteModelAdmin): summernote_fields = ('blog_post',) admin.site.register(Post, PostAdmin)Run : C:\django\blogsite>python manage.py runserver