Python Django Zinnia is a great alternative for WordPress, especially for those who don’t want to use PHP but Python for web development.
It uses Django which is a web application framework for python web app development. Django comes with a little web server and uses sqlite for backend database server by default. Here, I leave a note on how to set up django zinnia on top of postgresql, nginx and python3 on Arch Linux
1. install necessary packages from repo.
pacman -S python python-pip nginx postgresql python-psycopg2
pacman -S libjpeg-turbo gcc
2. install python modules. don’t use uwsi from aur as it depends on python2 and some other unnecessary packages.
pip3 install django-blog-zinnia
pip3 install uwgsi
3. setup postgresql.
# Initialise DB
initdb --locale en_GB.UTF-8 -E UTF8 -D '/var/lib/postgres/data'
# disble cow (for btrfs filesystem)
chattr +C /var/lib/postgres/data
# start the service
exit
systemctl start postgresql
systemctl enable postgresql
# create user and db and grant privileges
createuser –interactive
createdb mydjangodb
# login to db
GRANT ALL PRIVILEGES ON DATABASE mydjangodb TO username;
\q
# change db config according to your needs
# default listen port is localhost:5432
/var/lib/postgres/data/postgresql.conf
3. Setup uwsgi.
# create directory
mkdir -p /etc/uwsgi/vassals
# Create emperor file (for managing multiple instances)
vim /etc/uwsgi/emperor.ini
[uwsgi]
emperor = /etc/uwsgi/vassals
uid = http
gid = http
# Create systemd unit file
vim /etc/systemd/system/emperor.uwsgi.service
[Unit]
Description=uWSGI Emperor
After=syslog.target
[Service]
ExecStart=/usr/bin/uwsgi –ini /etc/uwsgi/emperor.ini
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -INT $MAINPID
Restart=always
Type=notify
StandardError=syslog
NotifyAccess=all
KillSignal=SIGQUIT
[Install]
WantedBy=multi-user.target
3. change django db setting.
# Create django project under opt/django
mkdir -p /opt/django
cd /opt/django
django-admin.py startproject mysite
# Edit django a setting file, and change database setting.
vi mysite/mysite/settings.py
# DATABASES = {
# ‘default’: {
# ‘ENGINE’: ‘django.db.backends.sqlite3’,
# ‘NAME’: os.path.join(BASE_DIR, ‘db.sqlite3’),
# }
# }
Comment out like above and add lines below.
DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.postgresql’,
‘NAME’: ‘mydjangodb’,
‘USER’: ‘username’,
‘HOST’: ‘localhost’,
‘PORT’: ‘5432’,
}
}
4. set django and nginx
# Copy nginx uwsgi parameter files to django project directory.
cp /etc/nginx/uwsgi_params /opt/django/mysite/
# Create nginx config file under django project directory, and add the following.
vim /opt/django/mysite/mysite_nginx.conf
upstream django {
#server unix:///path/mysite.sock; # for using unix socket
server 127.0.0.1:8001;
}
server {
listen 8000;
server_name 127.0.0.1;
charset utf-8;
location /static {
alias /opt/django/mysite/static;
}
location / {
uwsgi_pass django;
include /opt/django/mysite/uwsgi_params;
}
}
# add below to http directive of nginx main config file
vim /etc/nginx/nginx.conf
include /etc/nginx/sites-enabled/*;
# Create nginx include config directory and link symbolically.
mkdir /etc/nginx/sites-enabled/
ln -s /opt/django/mysite/mysite_nginx.conf /etc/nginx/sites-enabled/
# Create uwsgi config file under django directory and link symbolically.
vim /opt/django/mysite/mysite_uwsgi.ini
[uwsgi]
chdir = /opt/django/mysite/
module = mysite.wsgi
master = true
processes = 4
socket = 127.0.0.1:8001
ln -s /opt/django/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
cp -R /usr/lib/python3.5/site-packages/django/contrib/admin/static /opt/django/mysite/
5. Start the services
systemctl start emperor.uwsgi
systemctl start nginx
Now, you should be able to access to http://127.0.0.1:8000/admin/
6. Set up django-zinnia-blog
Trying to figure out…
adding ‘tagging’ to INSTALLED APPS fails the program.
without adding ‘tagging’ app, i cannot load zinnia…
python is failing to load the following module. get_model method is not included in the source from the first place!
‘from django.db.models import get_model’
OK. I found a solution.
# back up the original python module.
cp -p /usr/lib/python3.5/site-packages/tagging/templatetags/tagging_tags.py /usr/lib/python3.5/site-packages/tagging/templatetags/tagging_tags.py.ori# Rewrite the module.
Comment the following line
# from django.db.models import get_modelAnd add the following lines.
from django.apps import apps
get_model = apps.get_model# Copy the static files
cp -Rp /usr/lib/python3.5/site-packages/zinnia/static/zinnia /opt/django/mysite/static/
mysite-nginx.conf look like this.
upstream django {
#server unix:///path/mysite.sock; # for using unix socket
server 127.0.0.1:8001;
}
server {
listen 80;
server_name 0.0.0.0;
charset utf-8;
location /static {
alias /opt/django/mysite/static;
}
location / {
uwsgi_pass django;
include /opt/django/mysite/uwsgi_params;
}
}
setting.py looks like this.
import os
gettext = lambda s: s
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydjangodb',
'USER': 'username',
'HOST': 'localhost',
'PORT': '5432',
}
}
TIME_ZONE = 'Asia/Tokyo'
STATIC_URL = '/static/'
STATIC_ROOT = '/opt/django/mysite/static/'
MEDIA_URL = '/media/'
SECRET_KEY = 'jo-1rzm(%sf)3#n+fb7h955yu$3(pt63abhi12_t7e^^5q8dyw'
USE_TZ = True
USE_I18N = True
USE_L10N = True
SITE_ID = 1
LANGUAGE_CODE = 'en'
ALLOWED_HOSTS = ('*')
LANGUAGES = (
('en', gettext('English')),
('fr', gettext('French')),
('de', gettext('German')),
('es', gettext('Spanish')),
('it', gettext('Italian')),
('nl', gettext('Dutch')),
('sl', gettext('Slovenian')),
('bg', gettext('Bulgarian')),
('hu', gettext('Hungarian')),
('cs', gettext('Czech')),
('sk', gettext('Slovak')),
('lt', gettext('Lithuanian')),
('ru', gettext('Russian')),
('pl', gettext('Polish')),
('eu', gettext('Basque')),
('he', gettext('Hebrew')),
('ca', gettext('Catalan')),
('tr', gettext('Turkish')),
('sv', gettext('Swedish')),
('is', gettext('Icelandic')),
('hr_HR', gettext('Croatian')),
('pt_BR', gettext('Brazilian Portuguese')),
('fa_IR', gettext('Persian')),
('fi_FI', gettext('Finnish')),
('uk_UA', gettext('Ukrainian')),
('zh-hans', gettext('Simplified Chinese')),
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.admindocs.middleware.XViewMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
ROOT_URLCONF = 'mysite.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.template.context_processors.i18n',
'django.template.context_processors.request',
'django.contrib.messages.context_processors.messages',
'zinnia.context_processors.version',
]
}
}
]
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.sitemaps',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.sites',
'django.contrib.admin',
'django.contrib.admindocs',
'django.contrib.staticfiles',
'django_comments',
'django_xmlrpc',
'mptt',
'tagging',
'zinnia'
)
from zinnia.xmlrpc import ZINNIA_XMLRPC_METHODS
XMLRPC_METHODS = ZINNIA_XMLRPC_METHODS
urls.py looks like this.
"""Urls for the demo of Zinnia"""
from django.conf import settings
from django.contrib import admin
from django.conf.urls import url
from django.conf.urls import include
# from django.views.static import serve
from django.views.defaults import bad_request
from django.views.defaults import server_error
from django.views.defaults import page_not_found
from django.views.defaults import permission_denied
from django.views.generic.base import RedirectView
from django.contrib.sitemaps.views import index
from django.contrib.sitemaps.views import sitemap
from django_xmlrpc.views import handle_xmlrpc
from zinnia.sitemaps import TagSitemap
from zinnia.sitemaps import EntrySitemap
from zinnia.sitemaps import CategorySitemap
from zinnia.sitemaps import AuthorSitemap
urlpatterns = [
url(r'^$', RedirectView.as_view(url='/blog/', permanent=True)),
url(r'^blog/', include('zinnia.urls', namespace='zinnia')),
url(r'^comments/', include('django_comments.urls')),
url(r'^xmlrpc/$', handle_xmlrpc),
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),
]
sitemaps = {
'tags': TagSitemap,
'blog': EntrySitemap,
'authors': AuthorSitemap,
'categories': CategorySitemap
}
urlpatterns += [
url(r'^sitemap.xml$',
index,
{'sitemaps': sitemaps}),
url(r'^sitemap-(?P<section>.+)\.xml$',
sitemap,
{'sitemaps': sitemaps}),
]
urlpatterns += [
url(r'^400/$', bad_request),
url(r'^403/$', permission_denied),
url(r'^404/$', page_not_found),
url(r'^500/$', server_error),
]
if settings.DEBUG:
urlpatterns += [
url(r'^media/(?P<path>.*)$', serve,
{'document_root': settings.MEDIA_ROOT})
]
7. Sync DB and Restart the uwsgi service and access the site.
Now you should build your website and webapps. Change urls according to your needs.
python manage.py migrate
systemctl restart emperor.uwsgi
Access http://127.0.0.1:8000/blog
8. Restrict access to admin page