We finally switched from WordPress to Django

11 min read

With great pleasure, I will like to announce that djangocentral is now powered by Django, and I couldn't be more satisfied with the results. 

I was intending to do this switch for a very long time, but I was always busy with other things.

But before someone asks, your blog is about Django then why did you use WordPress in the first place?

To answer that, we need to go back in time. 

Backstory 

I would like to start by introducing myself. I am Abhijeet Pal, a software engineer from India. I spend most of my time working with Python and Django.

I started djangocentral during my college days in 2018 but it was not my first blog. Prior to this, I had a couple of more sites that I built to fetch some money to pay my bills.

The sites that I ran before djangocentral were on WordPress because WordPress by its very nature was a dead-simple blogging platform. I eventually got bored of all the old sites since I was not really writing about things that I enjoy. 

See the story is pretty long I will not go into the details as I am trying to keep it as short as possible.

In essence, things happened --> one thing led to another --> suddenly one day I am on the path of learning Python, Django. 

While I was learning all these, I struggled a lot to find satisfactory solutions to my dumb questions.

Note that I am talking about 2018 when Django just released Django 2 with some major upgrades.  Python2 vs Python 3 was still a thing. The StackOverflow answers were very helpful but sometimes outdated. All this to a beginner was way too overwhelming.

So I decided that the best way to learn something is by writing about it. And that's how in 2018 I bought the domain and decided to write articles about the stuff that I struggled to find or the things that I newly discovered.

Since at this point I was a very naive programmer I decided to use WordPress as a platform to write articles because I was already familiar with it.

The most ironic part of all this is that one of the most popular articles in my blog was "Building A Blog Application With Django" and yes I did receive comments asking why a site about Django is running on WordPress/PHP

The angry gentleman on the internet asked a very reasonable question in my defense I was just avoiding building something that's already built.

A similar discussion took place at the official Django forum where someone asked why the official forum is running on discourse.

Why move away from WordPress?

I don't think any other CMS has a community as large and active as WordPress. You can find a plugin for literally anything and because of the same reason WordPress is also the most vulnerable to attacks. It's not uncommon to hear that a new vulnerability has been discovered on some popular plugin every other month. 

But the most tempting issue for me was that since I do not write PHP nor I was thrilled to learn it, I was always dependent on plugins for basic tasks and it limited my ability to expand djangocentral beyond a blog. 

As far as blogging is concerned, I don't even write articles frequently anymore for me djangocentral served as gigantic personal documentation. Often at work, I google my articles to solve a lot of trivial tasks. 

Now last year I launched another site madewithdjango.com which was a collection of websites made with Django.  At this point, I have to manage two sites and was paying hosting charges for both.

Then a few months later I decided it was time to merge the two sites. 

Django CMS, Wagtail, and others...

I am not using any of them but I can vouch that for most use cases these brilliant open source projects will be sufficient. 

I was considering using wagtail for a while but the tree approach they use for storing pages was not something I was looking for. 

How hard can it be to build a blog from scratch?

Not much, you need an article/post model and a category model register them in admin, create templates, and you are done. 

While that works you need to go an extra mile to make it production-ready.

Before we go into the details of it let's quickly go over the other parts of my stack. 

PostgreSQL

Django and Postgres go hand in hand. While Django does support a lot of other databases, Postgres is the most popular one within the community. Postgresql is also one of the if not the most advanced relational databases.

For this project, I am using the latest PostgreSQL 14 which has some major performance improvements over the previous version.

Although, I was considering using MariaDB since it's lighter but I didn't find the difference large enough to justify the switch. 

Poetry

Package management in Python is not one of the best out there. Lots of tools have tried to solve this issue such as pipenv, pdm, poetry, pip-tools etc.

Even though poetry is comparatively new it is one of the best solutions out there. I strongly recommend using it.

Gunicorn 

There are a number of WSGI servers out there, I find gunicorn straightforward and easy to set up. Gunicorn can run the app with different kinds of workers as well such as sync, gthread and async. 

I am running with the conventional 2*number_of_core + 1 worker with 2 threads in each configuration. 

Tailwind CSS 

There are hundreds of UI frameworks out there. You can choose from any of them. I have used Tailwind CSS for the UI and I have been loving it so far. 

Shout out to django-tailwind that makes integration with Tailwind CSS super easy for Django projects.

The Design of Djangocentral is heavily inspired by these two amazing open-source projects. 

  1. https://github.com/mertJF/tailblocks
  2. https://github.com/timlrx/tailwind-nextjs-starter-blog

Redis 

Django comes with a caching backend but you do need to configure it to work with Redis. Caching is one of the most important parts of any application. A good cache policy ensures better performance. The most critical part of any caching system is cache invalidation and it can get really tricky with hard-to-catch bugs.

There is a package called django-cachalot that caches ORM queries and automatically invalidates them when you save/update/delete an object it's a great solution for applications like this where you will have lot more reads than writes.

Migrating data

This is probably the most integral part of the migration. I guess this is something that stops a lot of people from switching from WordPress to Django because they don't wanna lose their data.

While there is no super-easy way to do this I used the following approach -

  1. Export all the post data from WordPress in an XML
  2. Parse the file
  3. Populate the database

There are some good examples that you can find in Github. Here is the script that I made for my project I believe it should be enough to get you started.

Comments

Comments are an essential part of any blog. Not only do they allow people to share their thoughts and opinions but often someone points out typos or errors in your posts.

A commenting system is not as easy as it sounds. For example, say you made a comment model that relates to the post model in one-to-many relationship. This approach does work but you can not have nested comments in it. 

To solve that one might add a foreign key to self, and that would work. But in situations where you have a lot of nested comments and this will directly impact your load time. 

To solve this new problem one may want to use MPPT that allows storing tree like data in SQL which makes reads faster almost in constant time. The downside is that inserting and moving items gets expensive and the cost increases linearly with the size of the tree.

Even if you get all this right, you now have to invest some of your valuable time in moderating comments. Trust me you will receive a lot of spam comments. You also need to put some kind of authentication on your comments so that your blog does not get bombed by scripts.

Not to forget restoring comments from WordPress XML into Django is a wild task in itself.

The easiest way to do dodge all this is to use disqus. They have a free package that can import comments, auto moderate, and much more. The criticism against them is that they show ads and sell out your data.

I did consider using some free, no ads, privacy-friendly alternatives like  utterances which uses GitHub issues for comments but the argument against them is that it's abusing the GitHub API and GitHub can take it down any day.

The production-grade blog checklist 

Your production-grade blog must have - 

  1.  Admin dashboard
  2.  WYSIWYG Editor
  3.  Database and media backup strategy 
  4.  Feeds
  5.  Comments
  6.  Sitemap
  7.  caching system 
  8.  SEO 
  9.  Effective deployment strategy
  10. Dark mode? maybe 

Admin dashboard

Django admin is one of the most useful utility of Django. 

Although, Design is subjective if you are coming from WordPress you might not enjoy the UI. Luckily the Django community has made a lot of themes that you can use over the admin site. 

django-jazzmin is my favourite, some configurations and CSS tweaks and this is how my admin dashboard looks.


You can tell I like dark mode. If you want to know more about my admin stylesheet read - Making Django Admin Jazzy With django-jazzmin  

WYSIWYG Editor

Choosing the right editor can be confusing. I have used a lot of different editors and I finally settled with summernote.

Specifically, because it's out-of-the-box media support and it's also very easy to use. The best part about summernote is you do need to make changes in your database column to use it. You can simply define the summernote field in the ModelAdmin and you are good to go.

Check out - Integrating Summernote WYSIWYG Editor in Django

Database and media backup

One of the things I admire most about WordPress is the ability to migrate hosts in a matter of clicks. Unfortunately, something similar does not exist in the Django ecosystem. However, there are tools that you can use for migrating the data.

django-import-export - Allows you to import-export data from the admin. 

django-dbbackup - A great tool to backup your entire database. It also has a media backup utility that works flawlessly for media files. Unfortunately restoring on fresh database can be problematic there is an open issue about it - https://github.com/jazzband/django-dbbackup/issues/245 

Postgres pd_dump and pg_restore - The old-school way to backup your database. This is what I do for dockerized Postgres backup and restoration.

# Backup
docker exec -t postgres-container pg_dumpall --data-only -U postgres > dump.sql

#Restore
cat dump.sql | docker exec -i postgres-container psql -U postgres 

RDS and S3

Since we are on the subject of database and media files. The best practice is to use a managed database service such as RDS and for media files storage services like S3 and the static files go through CDN. 

However, all these add extra costs to your infrastructure. If you can afford that go for it hands down. My goal was to keep the system as affordable as my WordPress installation therefore I am hosting everything locally and using Nginx to serve media and static files.

SEO 

What's the point of writing blogs if no one is going to read them? With SEO you can make sure that your blog is indexed by search engines and hopefully get some decent traffic. The bare minimum requirement is to have a meta-title and a meta-description you can use django-meta for it.

Feeds and Sitemaps

Sitemaps direct search engines to crawl your website and make sure that they know about all the pages. 

RSS feeds are a great way to keep your readers up to date with your blog.

Luckily Django has built-in support for both of them. 

  1. Creating Feeds with Django
  2. Creating Sitemaps in Django

Is it worth all the trouble?

At this one point one may ask, is it worth all the trouble?

Well, I ran some speed tests on both and every time the Django application appears to be perform better than the WordPress one.

WordPress


Django 



The same page was tested with ads disabled.

The response time is now roughly 57% quicker and the page size got reduced by 78%. 

Note that the results do not imply that one language/framework is faster than the other. It just means my WordPress setup was poor. Moreover, as I discussed earlier I made the switch primarily because of my expertise with Python. The performance gain is just the icing on the cake.

FAQ

1] Did you lose traffic because of the switch?

- It's too early to comment on this, but there is no drop in my traffic yet.

2] Is there something you miss about WordPress?

- Probably the classic editor it was well built for writing articles. However, summernote isn't bad either.

3]  Do you use Docker in production?

- Yes I do.

What's next for djangocentral

You can clearly tell the site is no more just another blog. My vision is to make it a one-stop solution for all your Django related resources. I have pledged to write more articles and expand the site further.

I will also add some of the common web development tools here. One such tool already exists and this was something I needed dearly.  Most people use notepad for this but I like keeping a tab of hastebin open to paste anything that I might need later now I use https://djangocentral.com/tools/notepad/

Wrapping up

There are still a lot of things to cover I might write about them later. I hope this is enough to get you started with your blog.

If you have any suggestions, questions or feedback please feel free to contact me or drop a comment below.

Finally a big thanks to the open-source projects that made my life easier.

  1. Django
  2. django-cachalot 
  3. django-taggit
  4. django-meta
  5. django-summernote
  6. django-extensions
  7. django-debug-toolbar
  8. django-tailwind
  9. pytest-django
  10. django-jazzmin
  11. django-maintenance-mode

and many more ...


DJANGO
author's image
Abhijeet Pal Author and Editor in Chief @djangocentral

Abhijeet is a full-stack software developer from India with a strong focus on backend and system design. He is driven by the need to create impactful solutions that add value to the internet in any way possible.

LinkedIn Twitter Github

Latest Articles

Latest from djangocentral

Django 4.1 adds async-compatible interface to QuerySet

The much-awaited pull request for an async-compatible interface to Queryset just got merged into the main branch of Django.Pull Request - https://github.com/django/django/pull/14843 The Django core team has been progressively adding async suppor…
Read more →

3 min read

Making Django Admin Jazzy With django-jazzmin

Django admin is undoubtedly one of the most useful apps of Django. Over the years there has been very little change in the admin app as far as the UX is concerned and it's not a bad thing at all. Django admin was designed to provide a simple and minimali…
Read more →

4 min read