Django MEZZANINE Blog Setup


In the previous post, I wrote how to set up django zinnia blog.
However, django-zinnia-blog documentation is not really intended for django beginners and you have read the source to know how it works.
I only know the basic, I could not properly override urls and templates, because it seemed django-zinnia-blog apps depend on each other and it was difficult to just deploy the app I want from it.
Mezzanine is a CMS but has a blog functionality as well. And most importantly it has very rich documentation. It's got even FAQ! It also explains how to override templates.:) I think it comes with caching functionality, too because it performs a lot faster than zinnia with demo setup.
A following note is how to have mezzaine running on arch linux with nginx and postgresql and uwsgi. I choose arch linux, because it is the simplest distribution I know and it's bleeding edge which I think makes the OS more secure. Systemd works wonderfully as well. Nginx and postgresql are chosen just because I feel they are a lot lighter than other alternatives. I alway choose uwsgi because it is simple enough and people say it performs the best among python application servers.

1. Nesessary packages. (from Arch linux repository and pip)
pacman -S python-pip libjpeg-turbo gcc nginx postgresql
pip install mezzanine
pip install uwsgi

2. DB (btrfs)
su - postgres
initdb --locale en_GB.UTF-8 -E UTF8 -D '/var/lib/postgres/data/'
chattr +C /var/lib/postgres/data

su -
systemctl start postgresql
systemctl enable postgresql

su - postgres
createuser --interactive
createdb myblogdb

psql
GRANT ALL ON DATABASE myblogdb TO root;
\q

3. uwsgi
mkdir -p /etc/uwsgi/vassals

vim /etc/uwsgi/emperor.ini
#--------------------------#
[uwsgi]
emperor = /etc/uwsgi/vassals
uid = http
gid = http
#--------------------------#

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
#--------------------------#

4. MEZZANINE
mkdir /opt/mezzanine
cd /opt/mezzanine
mezzanine-project myblog
cd myblog

vim myblog/local_settings.py
#--------------------------#

DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.postgresql',
    'NAME': 'myblogdb',
    'USER': 'root',
    'HOST': 'localhost',
    'PORT': '5432',
    }
}
#--------------------------#

cp -p /usr/lib/python3.5/site-packages/mezzanine/utils/html.py /usr/lib/python3.5/site-packages/mezzanine/utils/html.py.ori

vim /usr/lib/python3.5/site-packages/mezzanine/utils/html.py
#--------------------------#
remove ,HTMLParseError from the forth line.
#--------------------------#

mkdir -p static/media
python manage.py collectstatic
python manage.py migrate

5. nginx conf for mezzanine. (Example. I recommend going full ssl.)
vim /opt/mezzanine/myblog/myblog_nginx.conf
#--------------------------#
upstream django {
    server 127.0.0.1:8001;
}

server {
    listen 80;
    server_name 0.0.0.0;
    charset utf-8;

    location /static {
        alias /opt/mezzanine/myblog/static;
    }

    location / {
        uwsgi_pass django;
        include /etc/nginx/uwsgi_params;
    }
#--------------------------#

mkdir /etc/nginx/sites-enabled/
ln -s /opt/mezzanine/myblog/myblog_nginx.conf /etc/nginx/sites-enabled/

6. uwsgi conf for mezzanine

vim /opt/mezzanine/myblog/myblog_uwsgi.ini
#--------------------------#
[uwsgi]
chdir = /opt/mezzanine/myblog/
module = myblog.wsgi
master = true
processes = 4
socket = 127.0.0.1:8001
#--------------------------#

ln -s /opt/mezzanine/myblog/myblog_uwsgi.ini /etc/uwsgi/vassals/

7. http directive main nginx conf.
vim /etc/nginx/nginx.conf
#--------------------------#
include /etc/nginx/sites-enabled/*;
#--------------------------#

8. change local_settings.py to
DEBUG = False
ALLOWED_HOSTS = ['*']

9. Create admin user for the django project, resync the db just in case.
python manage.py createsuperuser
python manage.py migrate

10. start service
systemctl restart nginx
systemctl restart postgresql
systemctl restart emperor.uwsgi
systemctl reenable nginx
systemctl reenable postgresql
systemctl reenable emperor.uwsgi

Advertisements

Starting Python Django Zinnia Blog

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