Learn to Code via Tutorials on Repl.it!

← Back to all posts
Django(Python & HTML) Coding Campus Tutorial #1 - Create a Blog with Django
h
DillonB07 (25)

Hello!

Here is a tutorial on creating a blog brought to you from Coding Campus I most definitely don't see anything wrong on the home page.

View on Coding Campus: https://codingcampus.ml/django-tutorial-create-blog/

Make sure that your repl is open when you create a post or do anything on the admin dashboard!

How to create a blog with Django and Python

In this tutorial I will be going through the process of creating a blog like this one using Replit. Replit is a free online IDE (Integrated Development Environment) that supports almost every programming language. For a list of languages that replit supports, go here: https://replit.com/languages. Now, let's get started!

Setting up your workspace

Sign into replit, or create an account if you haven't got one. Now, let's create a new repl called Blog.
You will need to open the packages menu in the sidebar and install django.

Now, open the Shell by pressing ⌘+⇧+S (Windows/Linux users use Ctrl(^) instead of ⌘)
Type in this commands to setup django: django-admin startproject mysite

You should now have the following files:

├── mysite
    ├── mysite
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   ├── wsgi.py
    ├── manage.py

Now, in the base directory create a file called .replit with these contents:

language = "python3"
run = "python mysite/manage.py runserver 0.0.0.0:3000"

This tells replit what to do when you press the run button as we aren't using the default main.py file.

Next, go to the setting.pyfile and add this line below the STATIC_URL declaration.

ALLOWED_HOSTS=['*']

Finally, there are three commands we need to run in the replit shell.

cd mysite

python manage.py makemigrations

python manage.py migrate

Now run your repl and click the open in new tab icon.
For some reason the embedded version of the site doesn't work so every time you make changes you will have to reload the page in a new tab. My site is here: https://Blog.dillonb07.repl.co

If your project is setup correctly, you should see this page:

Creating your blog

Now, we can start creating the actual blog.

Go back to the shell and run this command python manage.py startapp blog it will create a folder called blog.

├── db.sqlite3
├── mysite
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   ├── wsgi.py
├── manage.py
└── blog
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

Now, if you open your setting.py file, there should be a section that looks like this:

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

Add a new line to the end to initialize your blog app:

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

Go back to the shell and apply your migrations python manage.py migrate

Models

Go to models.py and replace the contents with this:

from django.db import models
from django.contrib.auth.models import User


STATUS = (
    (0,"Draft"),
    (1,"Publish")
)

class Post(models.Model):
    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
    updated_on = models.DateTimeField(auto_now= True)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=STATUS, default=0)

    class Meta:
        ordering = ['-created_on']

    def __str__(self):
        return self.title

Now, this will save your blog posts to a database.

We now need to make migrations and migrate them. Run python manage.py makemigrations and python manage.py migrate

Creating an Admin control panel

First, create a superuser in the shell. python manage.py createsuperuser
This will ask you for a username, email and password. For security purposes, you will not be able to see the passswords as you are typing them.

Run your repl and navigate to https://REPLNAME.USERNAME.repl.co/admin/ and log in with the details that you just created.

You should see a basic control panel. This is created from django.contrib.auth

Adding models to Admin site

Go to blog/admin.py and register the Post model that you created in the models.py file.

from django.contrib import admin
from .models import Post 

admin.site.register(Post)

Now, refresh the admin control panel and you should see a new section:

Press the Add button and you will be taken to this page:
Create a new post and make sure you selected the Publish option.

You will be redirected to the post list. However, it isn't very good. Go to admin.py and replace it with this code:

from django.contrib import admin
from .models import Post

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'status','created_on')
    list_filter = ("status",)
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
  
admin.site.register(Post, PostAdmin)

Refresh the page. It should now look like this: This is a lot better and easier to use.

Creating Views

Go to the views.py file and add the following code:

from django.views import generic
from .models import Post

class PostList(generic.ListView):
    queryset = Post.objects.filter(status=1).order_by('-created_on')
    template_name = 'index.html'

class PostDetail(generic.DetailView):
    model = Post
    template_name = 'post_detail.html'

This tells the website which blog posts we want to show. These are the ones with the public status.
If you have a look, you can see that it references two templates. index.html and post_detail.html. However, these don't exist yet. We will make these shortly.

Adding URLs

In your blog folder create a new file called urls.py. Add this code:

from . import views
from django.urls import path

urlpatterns = [
    path('', views.PostList.as_view(), name='home'),
    path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
]

This will create a URL for each published blog post. However, they aren't in the actual project yet. So, go to mysite/urls.py and add the new path to it.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]

Creating templates

Create a folder called templates in the first mysite folder. Now, modify your settings.py to locate the templates folder. After the from pathlib import Path line you will need to add import os.
Then, after this line,

BASE_DIR = Path(__file__).resolve().parent.parent

you need to add

TEMPLATES_DIRS = os.path.join(BASE_DIR,'templates')

Then, scroll down to the TEMPLATES whcih looks like this:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

Modify the 'DIRS': [], line to be 'DIRS': [TEMPLATES_DIRS],

Now, let's actually create the templates.
In the templates folder, add a base.html file. You will need to modify the bits that I have written in CAPITAL LETTERS.

<!DOCTYPE html>
<html>

    <head>
        <title>WEBSITE NAME</title>
        <link href="https://fonts.googleapis.com/css?family=Roboto:400,700" rel="stylesheet">
        <meta name="google" content="notranslate" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
            crossorigin="anonymous" />
    </head>

    <body>
        <style>
            body {
            font-family: "Roboto", sans-serif;
            font-size: 17px;
            background-color: #fdfdfd;
        }
        .shadow {
            box-shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.1);
        }
        .btn-danger {
            color: #fff;
            background-color: #f00000;
            border-color: #dc281e;
        }
        .masthead {
            background: #3398E1;
            height: auto;
            padding-bottom: 15px;
            box-shadow: 0 16px 48px #E3E7EB;
            padding-top: 10px;
        }
    </style>

        <!-- Navigation -->
        <nav class="navbar navbar-expand-lg navbar-light bg-light shadow" id="mainNav">
            <div class="container-fluid">
                <a class="navbar-brand" href="{% url 'home' %}">NAME OF YOur BLOG</a>
                <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive"
                    aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarResponsive">
                    <ul class="navbar-nav ml-auto">
                        <li class="nav-item text-black">
                            <a class="nav-link text-black font-weight-bold" href="YOUR ABOUT PAGE LINK">About</a>
                        </li>
                        <li class="nav-item text-black">
                            <a class="nav-link text-black font-weight-bold" href="YOUR CONTACT PAGE LINK">Contact</a>
                        </li>
                        <li class="nav-item text-black">
                            <a class="nav-link text-black font-weight-bold" href="YOUR STATUS PAGE LINK(I recommend uptimerobot)">Status</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
        {% block content %}
        <!-- Content Goes here -->
        {% endblock content %}
        <!-- Footer --> 
        <footer class="py-3 bg-grey">
            <p class="m-0 text-dark text-center ">Copyright &copy; YOUR NAME</p>
        </footer>
    </body>
</html>

This base.html file will be the main page that our viewers will see. The other templates will ad onto this page.

Let's create a sidebar.html file.

{% block sidebar %}

<style>
        .card{
            box-shadow: 0 16px 48px #E3E7EB;
        }
       
</style>

<!-- Sidebar Widgets Column -->
<div class="col-md-4 float-right ">
<div class="card my-4">
        <h5 class="card-header">About Us</h5>
    <div class="card-body">
        <p class="card-text"> This awesome blog is made with Python and Django! Watch out for a tutorial coming soon on how to make your own!</p>
        <a href="https://codingcampus.ml"
           class="btn btn-danger">Know more!</a>
    </div>
</div>
</div>

{% endblock sidebar %}

Now, it is time for the homepage!

Create a new template called index.html

{% extends "base.html" %} 
{% block content %}
<style>
    body {
        font-family: "Roboto", sans-serif;
        font-size: 18px;
        background-color: #fdfdfd;
    }
    
    .head_text {
        color: white;
    }
    
    .card {
        box-shadow: 0 16px 48px #E3E7EB;
    }
</style>

<header class="masthead">
    <div class="overlay"></div>
    <div class="container">
        <div class="row">
            <div class=" col-md-8 col-md-10 mx-auto">
                <div class="site-heading">
                    <h3 class=" site-heading my-4 mt-3 text-white"> Welcome to Coding Campus!</h3>
                    <p class="text-light">We love coding as much as you do! &nbsp
                    </p>
                </div>
            </div>
        </div>
    </div>
</header>
<div class="container">
    <div class="row">
        <!-- Blog Entries Column -->
        <div class="col-md-8 mt-3 left">
            {% for post in post_list %}
            <div class="card mb-4">
                <div class="card-body">
                    <h2 class="card-title">{{ post.title }}</h2>
                    <p class="card-text text-muted h6">{{ post.author }} | {{ post.created_on}} </p>
                    <p class="card-text">{{post.content|slice:":200" }}</p>
                    <a href="{% url 'post_detail' post.slug  %}" class="btn btn-primary">Read More &rarr;</a>
                </div>
            </div>
            {% endfor %}
        </div>
        {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}
    </div>
</div>
{% if is_paginated %}
  <nav aria-label="Page navigation conatiner"></nav>
  <ul class="pagination justify-content-center">
    {% if page_obj.has_previous %}
    <li><a href="?page={{ page_obj.previous_page_number }}" class="page-link">&laquo; PREV </a></li>
    {% endif %}
    {% if page_obj.has_next %}
    <li><a href="?page={{ page_obj.next_page_number }}" class="page-link"> NEXT &raquo;</a></li>

    {% endif %}
  </ul>
  </nav>
</div>
{% endif %}
{%endblock%}

This will be the main page that your viewers see. If you go back to your blog you will see that it has updated.

There is one last template to create.
Create a template called post_detail.html Add this code to it:

{% extends 'base.html' %} {% block content %}

<div class="container">
  <div class="row">
    <div class="col-md-8 card mb-4  mt-3 left  top">
      <div class="card-body">
        <h1>{% block title %} {{ object.title }} {% endblock title %}</h1>
        <p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
        <p class="card-text ">{{ object.content | safe }}</p>
      </div>
    </div>
    {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}
  </div>
</div>

{% endblock content %}

Your blog is now finished! If you want to add comments and a better system for creating blog posts, continue following the tutorial!

Comments

Comment model

Add this code to the bottom of blog/models.py

class Comment(models.Model):
    post = models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
    name = models.CharField(max_length=80)
    email = models.EmailField()
    body = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=False)

    class Meta:
        ordering = ['created_on']

    def __str__(self):
        return 'Comment {} by {}'.format(self.body, self.name)

In the console run the two commands for migrations. python manage.py makemigrations and python manage.py migrate

In admin.py replace the contents with the following code:

from django.contrib import admin
from .models import Post, Comment

class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'status','created_on')
    list_filter = ("status",)
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
  
admin.site.register(Post, PostAdmin)

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'body', 'post', 'created_on', 'active')
    list_filter = ('active', 'created_on')
    search_fields = ('name', 'email', 'body')
    actions = ['approve_comments']

    def approve_comments(self, request, queryset):
        queryset.update(active=True)

Now, if you log onto the admin dashboard, you should see a comments model.

Creating Comment Forms

In your blog folder, create a forms.py file.
This will be for letting visitors post their own comments to your blog posts.
Put this code inside:

from .models import Comment
from django import forms


class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ('name', 'email', 'body')

Now, modify your views.py file to look like this:

from django.views import generic
from .models import Post
from .forms import CommentForm
from django.shortcuts import render, get_object_or_404


class PostList(generic.ListView):
    queryset = Post.objects.filter(status=1).order_by("-created_on")
    template_name = "index.html"
    paginate_by = 3


def post_detail(request, slug):
    template_name = "post_detail.html"
    post = get_object_or_404(Post, slug=slug)
    comments = post.comments.filter(active=True).order_by("-created_on")
    new_comment = None
    # Comment posted
    if request.method == "POST":
        comment_form = CommentForm(data=request.POST)
        if comment_form.is_valid():

            # Create Comment object but don't save to database yet
            new_comment = comment_form.save(commit=False)
            # Assign the current post to the comment
            new_comment.post = post
            # Save the comment to the database
            new_comment.save()
    else:
        comment_form = CommentForm()

    return render(
        request,
        template_name,
        {
            "post": post,
            "comments": comments,
            "new_comment": new_comment,
            "comment_form": comment_form,
        },
    )

Now, we need to map the view in blog/urls.py. Replace the code in the file with this:

from . import views
from django.urls import path

urlpatterns = [
    path('', views.PostList.as_view(), name='home'),
#    path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
    path('<slug:slug>/', views.post_detail, name='post_detail')
]

Now, go to templates/post_detail.html and replace the code with this:

{% extends 'base.html' %} {% block content %}

<div class="container">
  <div class="row">
    <div class="col-md-8 card mb-4  mt-3 left  top">
      <div class="card-body">
        <h1>{% block title %} {{ post.title }} {% endblock title %}</h1>
        <p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
        <p class="card-text ">{{ post.content | safe }}</p>
      </div>
    </div>
    {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}

    <div class="col-md-8 card mb-4  mt-3 ">
      <div class="card-body">
        <!-- comments -->
        <h2>{{ comments.count }} comments</h2>

        {% for comment in comments %}
        <div class="comments" style="padding: 10px;">
          <p class="font-weight-bold">
            {{ comment.name }}
            <span class=" text-muted font-weight-normal">
              {{ comment.created_on }}
            </span>
          </p>
          {{ comment.body | linebreaks }}
        </div>
        {% endfor %}
      </div>
    </div>
    <div class="col-md-8 card mb-4  mt-3 ">
      <div class="card-body">
        {% if new_comment %}
        <div class="alert alert-success" role="alert">
          Your comment is awaiting moderation
        </div>
        {% else %}
        <h3>Leave a comment</h3>
        <form method="post" style="margin-top: 1.3em;">
          {{ comment_form.as_p }}
          {% csrf_token %}
          <button type="submit" class="btn btn-primary  btn-lg">Submit</button>
        </form>
        {% endif %}
      </div>
    </div>
  </div>
</div>
{% endblock content %}

This will create a form for the user to fill in. Then the comment will wait for you to approve it on the admin page.
Test it by going to a post detail page on your website.

Go and approve the comment on your admin page.

Going Crispy

Crispy Forms is a django library for forms. We are going to use it to update our comments.
Go to the Universal Package Manager and search django-crispy-forms.

Now, add it to the INSTALLED_APPS list in settings.py like this:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'crispy_forms'
]

Underneath STATIC_URL add this line:

CRISPY_TEMPLATE_PACK = 'bootstrap4'

Replace the contents of your templates/post_detail.html document with this:

{% extends 'base.html' %} {% block content %}
{% load crispy_forms_tags %}

<div class="container">
  <div class="row">
    <div class="col-md-8 card mb-4  mt-3 left  top">
      <div class="card-body">
        <h1>{% block title %} {{ post.title }} {% endblock title %}</h1>
        <p class=" text-muted">{{ post.author }} | {{ post.created_on }}</p>
        <p class="card-text ">{{ post.content | safe }}</p>
      </div>
    </div>
    {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %}

    <div class="col-md-8 card mb-4  mt-3 ">
      <div class="card-body">
        <!-- comments -->
        {% with comments.count as total_comments %}
        <h2>{{ total_comments }} comments</h2>

        <p>
          {% endwith %} {% for comment in comments %}
        </p>

        <div class="comments" style="padding: 10px;">
          <p class="font-weight-bold">
            {{ comment.name }}
            <span class=" text-muted font-weight-normal">
              {{ comment.created_on }}
            </span>
          </p>
          {{ comment.body | linebreaks }}
        </div>

        {% endfor %}
      </div>
    </div>
    <div class="col-md-8 card mb-4  mt-3 ">
      <div class="card-body">
        {% if new_comment %}
        <div class="alert alert-success" role="alert">
          Your comment is awaiting moderation
        </div>
        {% else %}
        <h3>Leave a comment</h3>
        <form method="post" style="margin-top: 1.3em;">
          {{ comment_form | crispy }}
          {% csrf_token %}
          <button type="submit" class="btn btn-primary  btn-lg">Submit</button>
        </form>
        {% endif %}
      </div>
    </div>
  </div>
</div>

{% endblock content %}

Rerun your repl and test the commenting system.

The commenting system is now finished!

Adding a WYSIWYG Editor

A WYSIWYG editor is an editor for your blog posts that allows you to change text formatting, and is a lot better than a plain text editor.

Go to the UPM and install django-summernote

In settings.py add summernot to your installed apps:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'crispy_forms',
    'django_summernote'
]

Now, go to mysite/urls.py and add a new path: path('summernote/', include('django_summernote.urls')),

Open settings.py and at the bottom, add this code

X_FRAME_OPTIONS = 'SAMEORIGIN'

# Base url to serve media files
MEDIA_URL = '/media/'

# Path where media is stored
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

Now, at the top add this code:

Also, replace your mysite/urls.py with this:

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('blog.urls')),
    path('summernote/', include('django_summernote.urls')),
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

Replace your admin.py with this code:

from django.contrib import admin
from django_summernote.admin import SummernoteModelAdmin
from .models import Post, Comment

class PostAdmin(SummernoteModelAdmin):
    list_display = ('title', 'slug', 'status','created_on')
    list_filter = ("status",)
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    summernote_fields = ('content',)
  
admin.site.register(Post, PostAdmin)

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ('name', 'body', 'post', 'created_on', 'active')
    list_filter = ('active', 'created_on')
    search_fields = ('name', 'email', 'body')
    actions = ['approve_comments']

    def approve_comments(self, request, queryset):
        queryset.update(active=True)

Now, let's run migrations.

python manage.py makemigrations && python manage.py migrate

Now, you have a WYSIWYG editor for your posts!

Sitemaps

Installing Sitemaps

Add django.contrib.sitemaps in INSTALLED_APPS setting.

Create a new file called sitemaps.py inside your blog folder with this code inside:

from django.contrib.sitemaps import Sitemap
from .models import Post

class PostSitemap(Sitemap):
    changefreq = "weekly"
    priority = 0.8

    def items(self):
        return Post.objects.filter(status=1)

    def lastmod(self, obj):
        return obj.updated_on

Go to mysite/urls.py and update it like so:

from django.contrib import admin
from django.contrib.sitemaps.views import sitemap
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from blog.sitemaps import PostSitemap

sitemaps = {
    'posts': PostSitemap,
}

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
    path('summernote/', include('django_summernote.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Inside the models.py file, update the Post class like this:

class Post(models.Model):
    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="blog_posts"
    )
    updated_on = models.DateTimeField(auto_now=True)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=STATUS, default=0)

    class Meta:
        ordering = ["-created_on"]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        from django.urls import reverse

        return reverse("post_detail", kwargs={"slug": str(self.slug)})

Then, create a new file called sitemaps.py inside the blog folder and add this code:

from django.contrib.sitemaps import Sitemap
from .models import Post

class PostSitemap(Sitemap):
    changefreq = "always"
    priority = 0.8

    def items(self):
        return Post.objects.filter(status=1)

    def lastmod(self, obj):
        return obj.updated_on

The changefreq = "always" can be updated to the following:
1. always
2. hourly
3. daily
4. weekly
5. monthly
6. yearly
7. never

This controls how fast the sitemap updates.

Finally, go to mysite/urls.py and make sure that it looks like this:

from django.contrib import admin
from django.contrib.sitemaps.views import sitemap
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from blog.sitemaps import PostSitemap

sitemaps = {
    'posts': PostSitemap,
}

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
    path('summernote/', include('django_summernote.urls')),
    path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
]
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Now, go to Blog.DillonB07.repl.co/sitemap.xml. Make sure to replace Blog and DillonB07 with your repl name and your username!

You should see something like this:

RSS Feeds

You have probably heard of RSS. RSS stands for Really Simple Syndication and it tells you when things are updated. So for example, you want to know whenever a new post is published you can use the RSS feed to have that information sent to you.

Creating our Feed

Let's start by creating a new file in the blog folder called feeds.py with this code:

from django.contrib.syndication.views import Feed
from django.template.defaultfilters import truncatewords
from .models import Post


class LatestPostsFeed(Feed):
   title = "My blog"
   link = ""
   description = "New posts of my blog."

   def items(self):
       return Post.objects.filter(status=1)

   def item_title(self, item):
       return item.title

   def item_description(self, item):
       return truncatewords(item.content, 30)

Now, we need to modify our blog/urls.py file to look like this:

from . import views
from django.urls import path
from .feeds import LatestPostsFeed

urlpatterns = [
    path('', views.PostList.as_view(), name='home'),
#    path('<slug:slug>/', views.PostDetail.as_view(), name='post_detail'),
    path('<slug:slug>/', views.post_detail, name='post_detail'),
    path('feed/rss', LatestPostsFeed(), name='post_feed')
    
]

Now, if you visit your website with /feed/rss on the end of the URL, you should see something like this:

Now, let's have a look at this and how you can use it.

Viewing our RSS Feed

Go to this website: https://rssatom.com/

Now, put in the link to your RSS Feed. In my case, it is https://blog.dillonb07.repl.co/feed/rss and then solve the reCAPTCHA.

When you search your feed, you get a screen like this:

Now, there is one last thing that we should do to our blog and that is to give it a custom domain name.

Custom Domain Names for FREE

You may not like the default address for your website and want something short like https://blog.ml We can easily add a custom domain to our blog by using Freenom

You will get to a website that looks like this:

Search for what you want your blog to be called in the box.

I'm going to search for exampleblog

⚠️⚠️IT IS IMPORTANT THAT YOU DO NOT CHOOSE A .tk DOMAIN OTHERWISE THIS WILL NOT WORK⚠️⚠️

Select the domain that you would like and click the Get it now! button. If it says Not available after click the button, search for the entire address in the search bar.

Click the Green Checkout button once you have chosen your domain.

Once the page loadsm, you will want to change the Period to 12 Months @ FREE. If you are paying, then you can change it to whatever you want to pay for.

Now, click Continue.

Agree to the Terms & Conditions and complete your order.

If you get a form then fill in all of the details (except your phone number) and then complete your order.

Once completed, you will need to go to your domains: https://my.freenom.com/clientarea.php?action=domains

Sign in and click Manage Domain then, Management Tools and Nameservers.

In a new tab, go to Cloudflare.com. Create an account if you don't have one and then enter the domain you chose from Freenom. If you do have an account, then you will need to go to your dashboard then add a new site.

Make sure that you select the Free plan.

Click Add record after it has scanned for DNS records.

Go back to Replit and click the pencil icon.

Now, input the domain you chose and press Copy.

Go back to Cloudflare and select CNAME in the Type section. Then, paste what you just copied into the Target section. In the name section, you will want to put your domain like this:

Press Save.

Now, repeat that process again, but put www as the Name

Click Continue.

You will be given two random nameservers in step 4. You need to go back to Freenom and select use custom nameservers

In the Nameserver 1 and Nameserver 2 boxes you need to put the nameservers you were given by Cloudflare.

After clicking Change nameservers you can close Freenom and return to Cloudflare.

Click Done, check nameservers

Follow the Quick Start Guide and make sure that everything is turned on.

Finally, go back to replit and press Continue
You should have a message like this. If you haven't, wait for up to 48 hours at the most. It normally works within a few minutes though.

Click Link Domain and everything is done!

Go to the domain you configured and you should see your blog. If you see an error that you got redirected to many times, just wait for a short while and it should show your blog when refreshed.

To see the demo blog I created, go to https://exampleblog.ml

Hope you enjoyed this tutorial!

If you want me to create another tutorial about how to get your website online 24/7, please comment down below!

Comments
hotnewtop
DillonB07 (25)

@ruiwenge2 Thanks! Not sure how to get my images to load on replit though

xfinnbar (149)

don't use django db it is not saved unless the editor is open

DillonB07 (25)

I don't think that is true @xfinnbar. I've created 2 posts on there both with the editor closed and they seem to still be there. Though, that may be because my reply is Always Online