This blog was created using a tutorial by John Elder (https://codemy.com/), which I will attach and highly recommend watching. In this transcript, I’ve included the code steps to make them easier to copy and paste, and I’ve also updated everything for Python 3.13 in 2025.
(1) I installed Python
Installs the Python interpreter needed to run Django and your scripts.
(2) I went to the directory using cd C:\DjangoBlog or mkdir C:\DjangoBlog and cd C:\DjangoBlog if you don’t have this folder
Moves to your project folder, or creates one if it doesn’t exist.
(3) python -m venv virt to create a virtual environment
Creates an isolated environment named virt so Python packages for this project don’t affect other projects.
(4) virt\Scripts\activate to activate it
Turns on the virtual environment; now python and pip refer to this project’s environment.
(5) django-admin startproject ablog
Creates a new Django project called ablog with default settings and structure.
(6) cd ablog/ to go to this folder
Navigates into the project folder where main Django files are located.
(7) dir to see the list of files in the folder
Lists files to confirm your project structure is created.
(8) python manage.py runserver
Starts the built-in Django web server to test your project locally.
(9) Go to your browser and visit the URL commander provided http://127.0.0.1:8000/. You will see the Django starting screen
Confirms your project is running successfully.
(10) Press Ctrl+C to break the server
Stops the local Django server so you can make further changes.
(11) python manage.py createsuperuser. Type your name, email, password
Creates an admin user for logging into the Django admin interface.
(12) python manage.py startapp theblog
Creates a new Django app called theblog for your blog logic.
(13) In Sublime editor, find the folder ablog and add 'theblog' to the INSTALLED_APPS list
Registers your app with the project so Django knows it exists.
(14) In urls.py, change the import from
from django.urls import path
to
from django.urls import path, include
Allows you to include URLs from your app into the main project routes.
(15) Add the element to urlpatterns
path('', include('theblog.urls')),
Routes the home URL to your blog app instead of the default page.
(16) Create a new file in the theblog folder named urls.py
This file will define URL routes specific to the blog app.
(17) Insert the next code:
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home')
]
Maps the root URL of the app to the home view function.
(18) In the views.py file, insert:
from django.shortcuts import render
def home(request):
return render(request, 'home.html', {})
Defines a function that returns the home.html template when someone visits the home page.
(19) Create a new folder in theblog named templates
Where HTML templates for the app will be stored.
(20) Create a home.html file
This file will be displayed when the home view is called.
(21) Insert the next code in theblog/models.py:
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
def __str__(self):
return self.title + ' | ' + str(self.author)
Defines a Post model with title, author, and body fields. ForeignKey links to a user. __str__ shows a readable string in admin.
(22) Insert the next code in theblog/admin.py:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Makes the Post model editable in the Django admin interface.
(23) Run python manage.py makemigrations
Prepares database changes for the new Post model.
(24) Run python manage.py migrate
Applies changes to the database, creating tables for your models.
(25) Run python manage.py runserver
Starts the server again so you can see the updated app.
(26) Visit http://127.0.0.1:8000/admin/. You now should be able to create posts and access the list of posts
You can now log in to the admin, add blog posts, and manage them.
--------------------------
(27) Insert the next code in theblog/views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post
class HomeView(ListView):
model = Post
template_name = 'home.html'
(28) Change 'templates/home.html'
<h1>Post</h1>
<ul>
{% for post in object_list %}
<li>{{ post.title }} - {{ post.author.first_name }} {{ post.author.last_name }}<br/>
{{ post.body }}</li>
{% endfor%}
<ul>
(29) Change 'theblog/urls.py'
from django.urls import path
from .views import HomeView
urlpatterns = [
path('', HomeView.as_view(),name='home')
]
(30) Create 'templates/article_details.html'
(31) Change 'theblog/views.py'
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from .models import Post
class HomeView(ListView):
model = Post
template_name = 'home.html'
class ArticleDetailView(DetailView):
model = Post
template_name = 'article_details.html'
(32) Change 'theblog/urls.py'
from django.urls import path
from .views import HomeView, ArticleDetailView
urlpatterns = [
path('', HomeView.as_view(),name='home'),
path('article/<int:pk>',ArticleDetailView.as_view(),name='article-detail')
]
(33) Change 'home.html'
<h1>Post</h1>
<ul>
{% for post in object_list %}
<li><a href="{% url 'article-detail' post.pk%}">{{ post.title }}<a><br/>
{{ post.body }}</li>
{% endfor%}
<ul>
(34) Add the code to 'article_details.html'
<h1>{{ post.title }}</h1>
<small> By: {{ post.author.first_name}} {{ post.author.last_name }}</small><br/>
<hr>
<br/>
{{ post.body }}
<br/><br/>
<a href="{% url 'home' %}">Back</a>
(35) Create 'templates/base.html'
Go to https://getbootstrap.com/
Copy the quick start code
In case of v5.3
https://getbootstrap.com/docs/5.3/getting-started/introduction/
The code is:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>
<body>
{% block content %}
{% endblock %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
</body>
</html>
(36) Change 'home.html'
{% extends 'base.html' %}
{% block content %}
<h1>Post</h1>
<ul>
{% for post in object_list %}
<li><a href="{% url 'article-detail' post.pk%}">{{ post.title }}<a><br/>
{{ post.body }}</li>
{% endfor%}
<ul>
{% endblock %}
(37) Add this code to have a left indent and one break line before the content
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
</head>
<body>
<br/>
<div class="container">
{% block content %}
{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
</body>
</html>
(38) Insert this part to article_details.html
{% extends 'base.html' %}
{% block content %}
...
{% endblock %}
(39) Insert navigation bar to 'base.html'
<nav class="navbar navbar-expand-lg bg-success-subtle">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">PValues Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
</ul>
</div>
</div>
</nav>
(40) Change <title></title> in 'base.html'
<title>
{% block title %}
PValues Blog
{% endblock %}
</title>
(41) Insert the code to have another title
{% block title %}
{{post.title}} - PValues Blog
{% endblock %}
(42) Insert title_tag to models.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=255)
title_tag = models.CharField(max_length=255,default = "")
author = models.ForeignKey(User,on_delete=models.CASCADE)
body = models.TextField()
def __str__(self):
return self.title + ' | ' + str(self.author)
(43) Make migrations
Ctrl+C
python manage.py makemigrations
python manage.py migrate
(44) Change the title in article_details.html
{% block title %}
{{post.title_tag}} - PValues Blog
{% endblock %}
Now you can have a custom title
(45) Insert meta tag to base.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="keywords" content="{{ post.keywords }}">
<title>
{% block title %}
PValues Blog
{% endblock %}
</title>
(46) Add keywords to model.py
from django.db import models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=255)
title_tag = models.CharField(max_length=255,default = "")
keywords = models.CharField(max_length=255, blank=True, null=True)
author = models.ForeignKey(User,on_delete=models.CASCADE)
body = models.TextField()
def __str__(self):
return self.title + ' | ' + str(self.author)
(47) Make migrations
Ctrl+C
python manage.py makemigrations
python manage.py migrate
(48) Add description meta tag
base.html <meta name="description" content="{{ post.description }}">
model.py description = models.CharField(max_length=255, blank=True, null=True)
Make migrations
(49) Change views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView
from .models import Post
class HomeView(ListView):
model = Post
template_name = 'home.html'
class ArticleDetailView(DetailView):
model = Post
template_name = 'article_details.html'
class AddPostView(CreateView):
model = Post
template_name = 'add_post.html'
fields = '__all__'
(50) Add add_post to urls
from django.urls import path
from .views import HomeView, ArticleDetailView, AddPostView
urlpatterns = [
path('', HomeView.as_view(),name='home'),
path('article/<int:pk>',ArticleDetailView.as_view(),name='article-detail'),
path('add_post/',AddPostView.as_view(),name='add_post'),
]
(51) Create add_post.html
{% extends 'base.html' %}
{% block content %}
<h1>Add Blog Post</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Post</button>
{% endblock %}
(52) Change models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=255)
title_tag = models.CharField(max_length=255,default = "")
keywords = models.CharField(max_length=255, blank=True, null=True)
description = models.CharField(max_length=255, blank=True, null=True)
author = models.ForeignKey(User,on_delete=models.CASCADE)
body = models.TextField()
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
return reverse('article-detail', args=[str(self.id)])
(53) Create forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'title_tag', 'body')
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'title_tag': forms.TextInput(attrs={'class': 'form-control'}),
'body': forms.Textarea(attrs={'class': 'form-control'}),
}
(54) Change add_post.html
{% extends 'base.html' %}
{% block content %}
<h1>Add Blog Post</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Post</button>
</div>
{% endblock %}
(55) Change views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView
from .models import Post
from .forms import PostForm
class HomeView(ListView):
model = Post
template_name = 'home.html'
class ArticleDetailView(DetailView):
model = Post
template_name = 'article_details.html'
class AddPostView(CreateView):
model = Post
form_class = PostForm
template_name = 'add_post.html'
(56) You can also have placeholders in forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'title_tag', 'body')
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control','placeholder':'Type the Title'}),
'title_tag': forms.TextInput(attrs={'class': 'form-control'}),
'body': forms.Textarea(attrs={'class': 'form-control'}),
}
(57) Create 'update_post.html'
{% extends 'base.html' %}
{% block content %}
<h1>Edit Blog Post</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Update</button>
</div>
{% endblock %}
(58) Make changes to views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView
class UpdatePostView(UpdateView):
model = Post
form_class = PostForm
template_name = 'update_post.html'
(59) Make changes to urls.py
from .views import HomeView, ArticleDetailView, AddPostView, UpdatePostView
urlpatterns = [
path('', HomeView.as_view(),name='home'),
path('article/<int:pk>',ArticleDetailView.as_view(),name='article-detail'),
path('add_post/',AddPostView.as_view(),name='add_post'),
path('article/edit/<int:pk>',UpdatePostView.as_view(),name='update-post'),
]
(60) Add buttons to article_details.html
<a class="btn btn-primary" href="{% url 'home' %}" role="button">Back</a>
<a class="btn btn-primary" href="{% url 'update-post' post.pk%}" role="button">Edit</a>
(61) Make changes to views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
...
class DeletePostView(UpdateView):
model = Post
template_name = 'delete_post.html'
(62) Make changes to delete_post.html
from django.urls import path
from .views import HomeView, ArticleDetailView, AddPostView, UpdatePostView, DeletePostView
urlpatterns = [
path('', HomeView.as_view(),name='home'),
path('article/<int:pk>',ArticleDetailView.as_view(),name='article-detail'),
path('add_post/',AddPostView.as_view(),name='add_post'),
path('article/edit/<int:pk>',UpdatePostView.as_view(),name='update-post'),
path('article/<int:pk>/delete',DeletePostView.as_view(),name='delete-post'),
]
(63) Create delete_post.html
{% extends 'base.html' %}
{% block content %}
<h1>Delete Post</h1>
<h3>Delete: {{ post.title }}</h3>
<br/>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
<strong>Are you sure?!</strong><br/>
<button class="btn btn-danger">Delete Post!</button>
</div>
{% endblock %}
(63) Add the button to 'article_details.html'
<a class="btn btn-dark" href="{% url 'delete-post' post.pk%}" role="button">Delete</a>
(64) Make changes to views.py
from django.shortcuts import render
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Post
from .forms import PostForm
from django.urls import reverse_lazy
class HomeView(ListView):
model = Post
template_name = 'home.html'
class ArticleDetailView(DetailView):
model = Post
template_name = 'article_details.html'
class AddPostView(CreateView):
model = Post
form_class = PostForm
template_name = 'add_post.html'
class UpdatePostView(UpdateView):
model = Post
form_class = PostForm
template_name = 'update_post.html'
class DeletePostView(UpdateView):
model = Post
template_name = 'delete_post.html'
success_url = reverse_lazy('home')
(65) Make changes to views.py
class HomeView(ListView):
model = Post
template_name = 'home.html'
ordering = ['-id']
(66) Add dates to models.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from datetime import datetime, date
def get_default_user():
return User.objects.get(username='admin')
class Post(models.Model):
title = models.CharField(max_length=255)
title_tag = models.CharField(max_length=255,default = "")
keywords = models.CharField(max_length=255, blank=True, null=True)
description = models.CharField(max_length=255, blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=get_default_user)
body = models.TextField()
post_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
return reverse('article-detail', args=[str(self.id)])
(67) Stop server with Ctrl+C
Make migrations with python manage.py makemigrations
It will ask you to type the default value. Press enter if you want this date is today
Migrate everything with python manage.py migrate
(68) Insert date into article_details.html
{% extends 'base.html' %}
{% block title %}
{{post.title_tag}} - PValues Blog
{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<small> By: {{ post.author.first_name}} {{ post.author.last_name }} - {{ post.post_date }}</small>
<br/>
<hr>
<br/>
{{ post.body }}
<br/><br/>
<a class="btn btn-primary" href="{% url 'home' %}" role="button">Back</a>
<a class="btn btn-warning" href="{% url 'update-post' post.pk%}" role="button">Edit</a>
<a class="btn btn-dark" href="{% url 'delete-post' post.pk%}" role="button">Delete</a>
{% endblock %}
(69) Create a user authentifications app members by running this code in Commander
python manage.py startapp members
(70) Make changes to INSTALLED_APPS in 'ablog\settings.py'
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'theblog',
'members'
]
(71) Make changes to 'ablog\urls.py'
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('theblog.urls')),
path('members/',include('django.contrib.auth.urls')),
path('members/',include('members.urls')),
]
(72) Create 'members\templates' folder
(73) Create 'members\templates\login.html'
{% extends 'base.html' %}
{% block content %}
<h1>Login</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Login</button>
</div>
{% endblock %}
(74) Create 'members\templates\register.html'
{% extends 'base.html' %}
{% block content %}
<h1>Register</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Register</button>
</div>
{% endblock %}
(75) Make changes to 'members\views.py'
from django.shortcuts import render
from django.views import generic
from django.contrib.auth.forms import UserCreationForm
from django.urls import reverse_lazy
class UserRegisterView(generic.CreateView):
form_class = UserCreationForm
template_name = 'registration/register.html'
success_url = reverse_lazy('login')
(76) Create 'members\urls.py'
from django.urls import path
from .views import UserRegisterView
urlpatterns = [
path('register/',UserRegisterView.as_view(),name = 'register'),
]
(77) Make changes to base.html if you want to have Login and Register buttons in the navigation menu
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="{% url 'add_post' %}">Add Post</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
</ul>
(78) Add these two lines to 'ablog/settings.py' in the end of the code
LOGIN_REDIRECT_URL = 'home'
LOGOUT_REDIRECT_URL = 'home'
(79) Add logout button to the navigation bar in base.html
<li class="nav-item">
<form method="post" action="{% url 'logout' %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-link nav-link">
Logout
</button>
</form>
</li>
(80) Make changes to the navigation bar to reflect login and logout
<nav class="navbar navbar-expand-lg bg-success-subtle">
<div class="container-fluid">
<a class="navbar-brand" href="{% url 'home' %}">PValues Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
{% if user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'add_post' %}">Add Post</a>
</li>
<li class="nav-item">
<form method="post" action="{% url 'logout' %}" style="display:inline;">
{% csrf_token %}
<button type="submit" class="btn btn-link nav-link">
Logout
</button>
</form>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
(81) Make changes to every admin page you created
add_post.html
{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
<h1>Add Blog Post</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Post</button>
</div>
{% else %}
You can't be here and ruin my blog
{% endif}
{% endblock %}
article_details.html
{% extends 'base.html' %}
{% block title %}
{{post.title_tag}} - PValues Blog
{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<small> By: {{ post.author.first_name}} {{ post.author.last_name }} - {{ post.post_date }}</small>
<br/>
<hr>
<br/>
{{ post.body }}
<br/><br/>
<a class="btn btn-primary" href="{% url 'home' %}" role="button">Back</a>
{% if user.is_authenticated %}
<a class="btn btn-warning" href="{% url 'update-post' post.pk%}" role="button">Edit</a>
<a class="btn btn-dark" href="{% url 'delete-post' post.pk%}" role="button">Delete</a>
{% endif %}
{% endblock %}
delete_post.html
{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
<h1>Delete Post<h1>
<h3>Delete: {{ post.title }}</h3>
<br/>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
<strong>Are you sure?!</strong><br/>
<button class="btn btn-danger">Delete Post!</button>
</div>
{% else %}
You can't be here and ruin my blog
{% endif %}
{% endblock %}
update_post.html
{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
<h1>Edit Blog Post<h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.as_p}}
<button class="btn btn-primary">Update</button>
</div>
{% else %}
You can't be here and ruin my blog
{% endif %}
{% endblock %}
(82) Make changes to article_details to allow html running
{% extends 'base.html' %}
{% block title %}
{{post.title_tag}} - PValues Blog
{% endblock %}
{% block content %}
<h1>{{ post.title }}</h1>
<small> By: {{ post.author.first_name}} {{ post.author.last_name }} - {{ post.post_date }}</small>
<br/>
<hr>
<br/>
{{ post.body|safe }}
<br/><br/>
<a class="btn btn-primary" href="{% url 'home' %}" role="button">Back</a>
{% if user.is_authenticated %}
<a class="btn btn-warning" href="{% url 'update-post' post.pk%}" role="button">Edit</a>
<a class="btn btn-dark" href="{% url 'delete-post' post.pk%}" role="button">Delete</a>
{% endif %}
{% endblock %}
(83) I also edited home.html to have a description instead of post body
{% extends 'base.html' %}
{% block content %}
<h1>Post</h1>
<ul>
{% for post in object_list %}
<li><a href="{% url 'article-detail' post.pk%}">{{ post.title }}<a><br/>
{{ post.description }}</li>
{% endfor%}
<ul/>
{% endblock %}
(84) Install the library
pip install django-tinymce
(85) Add tinymce to settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'theblog',
'members',
'tinymce'
]
(86) Make changes to add_post.html and update_post.html by adding {{ form.media}}
Here's add_post.html
{% extends 'base.html' %}
{% block content %}
{% if user.is_authenticated %}
<h1>Add Blog Post</h1>
<div class = 'form-group'>
<form method="POST">
{% csrf_token %}
{{ form.media}}
{{ form.as_p}}
<button class="btn btn-primary">Post</button>
</div>
{% else %}
You can't be here and ruin my blog
{% endif %}
{% endblock %}
(87) Make changes to 'base.html', insert the code into head
{% load static %}
{{ form.media }}
(88) Make changes to 'forms.py'
from django import forms
from .models import Post
from tinymce.widgets import TinyMCE
class PostForm(forms.ModelForm):
body = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}))
class Meta:
model = Post
fields = ('title', 'title_tag', 'description','keywords','body')
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control','placeholder':'Type the Title'}),
'title_tag': forms.TextInput(attrs={'class': 'form-control'}),
'keywords': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.TextInput(attrs={'class': 'form-control'}),
'body': forms.Textarea(attrs={'class': 'form-control'})
}
(89) Make changes to model.py
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from datetime import datetime, date
from tinymce.models import HTMLField
def get_default_user():
return User.objects.get(username='admin')
class Category(models.Model):
name = models.CharField(max_legnth=255)
def __str__(self):
return self.username
def get_absolute_url(self):
return reverse('home')
class Post(models.Model):
title = models.CharField(max_length=255)
title_tag = models.CharField(max_length=255,default = "")
keywords = models.CharField(max_length=255, blank=True, null=True)
description = models.CharField(max_length=255, blank=True, null=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=get_default_user)
body = HTMLField(blank=True,null=True)
post_date = models.DateField(auto_now_add=True)
category = models.CharField(max_length=255)
def __str__(self):
return self.title + ' | ' + str(self.author)
def get_absolute_url(self):
return reverse('article-detail', args=[str(self.id)])