In this tutorial, I’ll guide you through the process of managing and replacing webpage content dynamically from the database using Django admin panel and django-tinymce. This approach is especially useful when you need to allow non-technical users to update webpage content without delving into the code.
Before we start! 🦸🏻
If you like this topic and you want to support me:
- Clap my article 50 times; that will really help me out.👏
- Follow me on Medium to get my latest article🫶
You can find the project full code under this repo:
https://github.com/tarek421995/Dynamic-Web-Pages.git
Step 1: Creating a Django Project
First, create a new Django project if you don’t already have one.
Create a Virtual Environment
It’s a good practice to use a virtual environment for your projects to manage dependencies. Here’s how to set up a virtual environment on different operating systems:
Linux and macOS
- Open your terminal.
- Navigate to your project directory.
- Create a virtual environment:
python3 -m venv venv
- Activate the virtual environment:
source venv/bin/activate
Windows
- Open your command prompt.
- Navigate to your project directory.
- Create a virtual environment:
python -m venv venv
- Activate the virtual environment:
.\venv\Scripts\activate
Install Django LTS
Once the virtual environment is activated, install Django LTS (Long Term Support version):
pip install django==5.2
Start the Django Project
- Create a new Django project:
django-admin startproject myproject cd myproject
- Create a new Django app within your project:
python manage.py startapp myapp
- Add
myapp
andtinymce
to yourINSTALLED_APPS
insettings.py
:
INSTALLED_APPS = [
...
'myapp',
'tinymce',
]
Step 2: Setting Up django-tinymce
Install django-tinymce:
pip install django-tinymce
Generate a requirements.txt
file to keep track of your dependencies:
pip freeze > requirements.txt
Update the template in settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": ['templates'], # add this ['templates'].
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
Configure TinyMCE in settings.py
:
TINYMCE_SPELLCHECKER = True
TINYMCE_EXTRA_MEDIA = {
'js': [
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js'
],
}
TINYMCE_DEFAULT_CONFIG = {
"height": "320px",
"width": "960px",
"menubar": "file edit view insert format tools table help",
"plugins": "advlist autolink lists link image charmap print preview anchor searchreplace visualblocks code "
"fullscreen insertdatetime media table paste code help wordcount spellchecker",
"toolbar": "undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft "
"aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor "
"backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap emoticons | "
"fullscreen preview save print | insertfile image media pageembed template link anchor codesample | "
"a11ycheck ltr rtl | showcomments addcomment code",
"custom_undo_redo_levels": 10,
'content_css': [
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css',
],
'content_js' : [
'https://cdn.jsdelivr.net/npm/bootstrap@5.0.0/dist/js/bootstrap.bundle.min.js'
]
}
import os
MEDIA_URL = '/media/'
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
Step 3: Using django-tinymce in Models
Let’s create a model for webpages that uses TinyMCE for content management.
from django.db import models
from tinymce.models import HTMLField
class Video(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
url = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class WebPage(models.Model):
title = models.CharField(max_length=200)
content = HTMLField(null=True, blank=True)
def __str__(self):
return self.title
# now create the tables in the database
python manage.py makemigrations
python manage.py migrate
Step 5: Create superuser
python manage.py createsuperuesr ## put your username and password
Step 5: Setting Up Django Admin
Register the WebPage
model in the Django admin to manage webpage content easily.
from django.contrib import admin
from .models import Video, WebPage
@admin.register(WebPage)
class WebPageAdmin(admin.ModelAdmin):
list_display = ('title',)
admin.site.register(Video)
Step 5: Add New content to HTMLField
Now login to django admin localhost:8000/admin with your username and password. Navigate to webpage model and create new one title = Web Developer Portfolio
<!-- Video Section -->
<div id="video" class="container my-5">
<h2 class="text-center mb-4">{video_app.Video.title}</h2>
<h2 class="text-center mb-4">{video_app.Video.description}</h2>
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item" src="{video_app.Video.url}" allowfullscreen="allowfullscreen"></iframe></div>
</div>
</div>
</div>
in the content field go to → tools → add source code and place this content:
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="#">Portfolio</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<a class="nav-link" href="#about">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#skills">Skills</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#projects">Projects</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#video">Video</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contact">Contact</a>
</li>
</ul>
</div>
</nav>
<!-- Jumbotron -->
<div class="jumbotron text-center bg-primary text-white">
<h1 class="display-4">John Doe</h1>
<p class="lead">A passionate Web Developer</p>
<hr class="my-4">
<p>Creating modern and responsive web applications</p>
<a class="btn btn-secondary btn-lg" href="#contact" role="button">Get in Touch</a>
</div>
<!-- About Section -->
<div id="about" class="container my-5">
<h2 class="text-center mb-4">About Me</h2>
<div class="row">
<div class="col-md-4 text-center">
<img src="https://via.placeholder.com/300" alt="About Me" class="img-fluid rounded-circle mb-3">
</div>
<div class="col-md-8">
<p>Hello! I'm John Doe, a web developer with a passion for creating beautiful and functional websites. With a background in computer science and several years of experience in the industry, I have honed my skills in front-end and back-end development, working with various technologies and frameworks.</p>
</div>
</div>
</div>
<!-- Skills Section -->
<div id="skills" class="container my-5">
<h2 class="text-center mb-4">Skills</h2>
<div class="row">
<div class="col-md-4">
<h3>Front-End</h3>
<ul class="list-unstyled">
<li>HTML/CSS</li>
<li>JavaScript</li>
<li>React</li>
<li>Bootstrap</li>
</ul>
</div>
<div class="col-md-4">
<h3>Back-End</h3>
<ul class="list-unstyled">
<li>Node.js</li>
<li>Express</li>
<li>Django</li>
<li>SQL</li>
</ul>
</div>
<div class="col-md-4">
<h3>Tools & Technologies</h3>
<ul class="list-unstyled">
<li>Git & GitHub</li>
<li>Docker</li>
<li>Kubernetes</li>
<li>AWS</li>
</ul>
</div>
</div>
</div>
<!-- Projects Section -->
<div id="projects" class="container my-5">
<h2 class="text-center mb-4">Projects</h2>
<div class="row">
<div class="col-md-4">
<div class="card">
<img src="https://via.placeholder.com/300" class="card-img-top" alt="Project 1">
<div class="card-body">
<h5 class="card-title">Project 1</h5>
<p class="card-text">Description of Project 1.</p>
<a href="#" class="btn btn-primary">View Project</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<img src="https://via.placeholder.com/300" class="card-img-top" alt="Project 2">
<div class="card-body">
<h5 class="card-title">Project 2</h5>
<p class="card-text">Description of Project 2.</p>
<a href="#" class="btn btn-primary">View Project</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<img src="https://via.placeholder.com/300" class="card-img-top" alt="Project 3">
<div class="card-body">
<h5 class="card-title">Project 3</h5>
<p class="card-text">Description of Project 3.</p>
<a href="#" class="btn btn-primary">View Project</a>
</div>
</div>
</div>
</div>
</div>
<!-- Video Section -->
<div id="video" class="container my-5">
<h2 class="text-center mb-4">{video_app.Video.title}</h2>
<h2 class="text-center mb-4">{video_app.Video.description}</h2>
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="embed-responsive embed-responsive-16by9"><iframe class="embed-responsive-item" src="{video_app.Video.url}" allowfullscreen="allowfullscreen"></iframe></div>
</div>
</div>
</div>
<!-- Contact Section -->
<div id="contact" class="container my-5">
<h2 class="text-center mb-4">Contact</h2>
<div class="row">
<div class="col-md-6 offset-md-3">
<form>
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" placeholder="Your Name">
</div>
<div class="form-group">
<label for="email">Email address</label>
<input type="email" class="form-control" id="email" placeholder="Your Email">
</div>
<div class="form-group">
<label for="message">Message</label>
<textarea class="form-control" id="message" rows="3" placeholder="Your Message"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
<!-- Footer -->
<div class="bg-dark text-white text-center py-3">
<p>© 2024 John Doe. All rights reserved.</p>
</div>
notice how we have replaced the content. {video_app.Video.title} ,{video_app.Video.description} directly from the model we have bluid
Step 3: Displaying Dynamic Content
Now, we’ll set up a view.py file to render the webpage content dynamically by replacing placeholders with data from the database.
# myapp/view.py
import re
from django.apps import apps
from django.utils.html import escape
from django.shortcuts import render
from .models import WebPage
def replace_model_placeholders(text):
pattern = re.compile(r'\{(\w+)\.(\w+)\.(\w+)\}')
def replace_with_model_data(match):
app_label, model_name, field_name = match.groups()
try:
model = apps.get_model(app_label, model_name)
instance = model.objects.first() # Or implement a more specific query
if not instance:
return "[Instance not found]"
value = getattr(instance, field_name, '[Field not found]')
return escape(str(value))
except LookupError:
return '[Model not found]'
except AttributeError:
return '[Field not found]'
except Exception as e:
return f'[Error: {str(e)}]'
return pattern.sub(replace_with_model_data, text)
def webpage_detail(request, pk):
webpage = WebPage.objects.get(pk=pk)
processed_content = replace_model_placeholders(webpage.content)
context = {'webpage': webpage, 'processed_content': processed_content}
return render(request, 'webpage_detail.html', context)
Step 3: Updating the Routes
Update the urls.py
file in your app (myapp/urls.py
):
from django.urls import path
from .views import webpage_detail
app_name = 'myapp'
urlpatterns = [
path('webpage/<int:pk>/', webpage_detail, name='webpage_detail'),
]
Ensure the app’s urls.py
is included in the project’s main urls.py
file (e.g., myprojec/urls.py
):
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')), # Include your app's URLs
]
Step 4: Creating the Template
Finally, create a template folder in the root directory and create file called webpage_detail.html to display the webpage content.
<!-- templates/webpage_detail.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ webpage.title }}</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<h1>{{ webpage.title }}</h1>
<div>{{ processed_content|safe }}</div>
<!-- Scripts -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>
now go to http://127.0.0.1:8080/1/
you can find the projects in this github repo https://github.com/tarek421995/Dynamic-Web-Pages.git
Conclusion
With these steps, you can now manage and dynamically replace webpage content using the Django admin panel and django-tinymce. This approach empowers non-technical users to update content easily and ensures that your webpages are always up-to-date with the latest data from your database.
Thank you for reading! If you found this guide helpful, please give it a clap and follow me for more insightful content on real-time data analytics and web development with Django. Your support keeps me motivated to share more valuable resources. Let’s stay connected!
Editor’s Note: ProSpexAi is a contributor-driven online publication and community dedicated to providing premier educational resources for data science, machine learning, and deep learning practitioners. We’re committed to supporting and inspiring developers and engineers from all walks of life.