PypiVersion Doc badge Pypi

Django-Leonardo

This is a stable for daily use in development.

A collection of awesome Django libraries, resources and shiny things. Full featured framework for building everything based on Django, FeinCMS, Horizon, Oscar and tons of another apps.

Don’t waste your time searching stable solution for daily problems.

Django-Leonardo

A collection of awesome Django libraries, resources and shiny things. Full featured framework for building everything based on Django, FeinCMS, Horizon, Oscar and tons of another apps.

Why

Python and Django communities are dynamic. Django is best framework for building web applications with tons apps, which provide additional futures. If you want make new web site really quick you must spend a lot of time for searching right libraries, integrating and configuring. Leonardo provide new module system which makes this much more easier than before. Leonardo solves cms, graph domains and provide stable core for easy extending and building whatever.

How it works

Leonardo loads all modules and gather their stuff. Extending is provided via consistent API which mirror settings of libraries and applications on which is based. Leonardo merge all keys securelly without duplicities.

Last thing in setup process is loading all stuff from local_settings.

These provide new features for us

  • no app settings required

  • one manage.py etc..

  • app dependencies:

    apps = [
        'leonardo_bootswatch',
        'leonardo_analytics', # this can be defined in other applications
    ]
    
  • autoinclude (Leonardo adds all Leonardo Modules to INSTALLED_APPS)

Installation

Installation

Installation Leonardo depend’s on your case. See some examples

Via PIP

pip install django-leonardo

# or latest

pip install git+https://github.com/django-leonardo/django-leonardo@develop#egg=leonardo

WGET one-liner

wget -O install_leonardo.sh https://github.com/django-leonardo/django-leonardo/raw/develop/contrib/scripts/install_leonardo.sh && sh install_leonardo.sh

Command by command

virtualenv -p /usr/bin/python2.7 leonardo_venv
cd leonardo_venv
. $PWD/bin/activate

pip install -e git+https://github.com/django-leonardo/django-leonardo@develop#egg=django-leonardo
pip install -r $PWD/src/django-leonardo/requirements.txt
django-admin startproject --template=https://github.com/django-leonardo/site-template/archive/master.zip myproject

export PYTHONPATH=$PWD/myproject
cd myproject

python manage.py makemigrations --noinput
python manage.py migrate --noinput
python manage.py bootstrap_site --url=http://raw.githubusercontent.com/django-leonardo/django-leonardo/develop/contrib/bootstrap/demo.yaml

echo "from django.contrib.auth.models import User; User.objects.create_superuser('root', 'mail@leonardo.cz', 'admin')" | python manage.py shell

python manage.py runserver 0.0.0.0:80

Using salt

With configured Salt use our Formula writte your pillars and run

salt-call state.sls leonardo

Bundles

Leonardo defines a group of bundles that can be used to install Leonardo and the dependencies for a given feature.

You can specify these in your requirements or on the pip comand-line by using brackets. Multiple bundles can be specified by separating them by commas.

For all Leonardo modules continue to https://github.com/leonardo-modules

$ pip install "django-leonardo[web]"

$ pip install "django-leonardo[web,nav,media,eshop]"

The following bundles are available:

CMS
  • django-leonardo[blog] - ElephantBlog integration
  • django-leonardo[folio] - Portfolio with translations
  • django-leonardo[multisite] - Leonardo multi sites
  • django-leonardo[forms] - Form Designer and Remote Forms
  • django-leonardo[links] - Links
  • django-leonardo[pagepermissions] - Page Permissions
Background Jobs
  • django-leonardo[celery] - Celery Workers for background Jobs
Admin
  • django-leonardo[admin] - Django Admin for Leonardo CMS
Auth
  • django-leonardo[auth] - All auth
  • django-leonardo[saml] - SAML auth backend
WYSIWYG Editors
  • django-leonardo[redactor] - Redactor
  • django-leonardo[summernote] - SummerNote
Themes
  • django-leonardo[themes] - Leonardo themes [Bootstrap, AdminLTE]
  • django-leonardo[adminlte] - AdminLTE theme
Ecommerce
  • django-leonardo[store] - Django-Oscar integration
  • django-leonardo[stores] - Django-Oscar Stores
  • django-leonardo[cod] - Django-Oscar Cash On Delivery Payment Method
Common
  • django-leonardo[sentry] - Raven integration with end-user friendly error page
  • django-leonardo[static] - AngularJS, React, BootStrap, D3.js, ..
  • django-leonardo[debug] - Debug toolbar
  • django-leonardo[tests] - Tools for testing
  • django-leonardo[redis] - Redis dep

Development Environment

Simplest way is using our SaltStack Formula where you can comfortably specify sources for leonardo with plugins.

Prerequisites

Prerequisites are installed on Ubuntu 12.04 LTS or Raspian Wheezy 7.0 with:

$ sudo apt-get install python-pip python-dev python-setuptools git python-virtualenv libtiff5-dev libjpeg8-dev zlib1g-dev \
    libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk

Prerequisites are installed on Ubuntu 14.04 LTS with:

$ sudo apt-get install python-pip python-dev python-setuptools git python-virtualenv libtiff5-dev libjpeg8-dev zlib1g-dev \
    libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk

Prerequisites are installed on Fedora 20 with:

$ sudo yum install python-pip python-dev python-setuptools git python-virtualenv libtiff-devel libjpeg-devel libzip-devel freetype-devel \
    lcms2-devel libwebp-devel tcl-devel tk-devel

Note

If you have problems with installation, please see SaltStack Formula where are all steps of installation.

usualy after successfuly installated prerequisites you can start with Leonardo

virtualenv -p /usr/bin/python2.7 /srv/leonardo/sites/mysite

git clone https://github.com/django-leonardo/django-leonardo.git -b develop /srv/leonardo/sites/mysite/leonardo

vim /srv/leonardo/sites/mysite/local_settings.py

put your config to local_settings.py:

# -*- coding: utf-8 -*-

from __future__ import absolute_import

import sys
from os.path import join, dirname, abspath, normpath

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'HOST': '127.0.0.1',
        'NAME': 'leonardo_mysite',
        'PASSWORD': 'mysite_password',
        'USER': 'leonardo_mysite'
    }
}

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
        'TIMEOUT': 120,
        'KEY_PREFIX': 'CACHE_MYSITE'
    }
}

SECRET_KEY = 'my_secret_key'

DEBUG = True

MEDIA_ROOT = '/srv/leonardo/sites/mysite/media/'
STATIC_ROOT = '/srv/leonardo/sites/mysite/static/'

TIME_ZONE = 'Europe/Prague'

LANGUAGE_CODE = 'en'

# your app here
APPS = [
    'blog',
    'forms',
    'news'
]

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'root': {
        'level': 'WARNING',
        'handlers': ['file'],
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        }
    },
    'formatters': {
        'verbose': {
            'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            'datefmt' : "%d/%b/%Y %H:%M:%S"
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'formatter': 'verbose'
        },
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/srv/leonardo/sites/mysite/leonardo_server.log',
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler',
            'formatter': 'simple',
        },
    },
    'loggers': {
        'django.request': {
            'handlers': ['mail_admins', 'file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}
Run

There are several options which you can use, see contrib directory in the repo https://github.com/django-leonardo/django-leonardo/tree/master/contrib

Django runserver

python /srv/leonardo/sites/mysite/leonardo/contrib/django/manage.py runserver 0.0.0.0:80

Tornado

python /srv/leonardo/sites/mysite/leonardo/contrib/tornado/server

Leonardo in Production

Leonardo in production is standard Django application. We use Gunicorn under Supervisor with Nginx proxy.

Supervisor

[program:leonardo_demo]
command=/srv/leonardo/sites/demo/leonardo/contrib/gunicorn/server
stdout_logfile=/srv/leonardo/sites/demo/logs/access.log
stderr_logfile=/srv/leonardo/sites/demo/error.log
user=leonardo
autostart=true
autorestart=true

Gunicorn

#!/bin/bash

NAME="leonardo_demo"
DJANGODIR=/srv/leonardo/sites/demo
USER=leonardo
GROUP=leonardo
NUM_WORKERS=3
DJANGO_SETTINGS_MODULE=leonardo.settings
DJANGO_WSGI_MODULE=wsgi

echo "Starting $NAME as `whoami`"

# Activate the virtual environment
cd $DJANGODIR
source /srv/leonardo/sites/demo/bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

# Start your Django Unicorn
# Programs meant to be run under supervisor should not daemonize themselves (do not use --daemon)
exec gunicorn ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --log-level=debug \
  --bind=0.0.0.0:9754

for Tornado see Github page

Nginx

upstream leonardo_server_leonardo_demo {
    server localhost:9754 fail_timeout=0;
}

server {
  listen 80;

  server_name demo.cms.robotice.cz;

  client_max_body_size 20M;

  access_log  /var/log/nginx/demo-access;
  error_log   /var/log/nginx/demo-error;

  keepalive_timeout 5;

gzip on;
gzip_min_length  1100;
gzip_buffers  4 32k;
gzip_types    text/plain application/x-javascript text/xml text/css;
gzip_vary on;

  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    if (!-f $request_filename) {
      proxy_pass http://leonardo_server_leonardo_demo;
      break;
    }
  }

  location /static {
    autoindex on;
    alias /srv/leonardo/sites/demo/static;
    expires    30d;
 }

  location /media {
    autoindex on;
    alias /srv/leonardo/sites/demo/media;
    expires    30d;
  }

}

Bootstrap site

We don’t repeat yourself and for really quick start with new site we provide simple API called Bootstrap which has simple format in yaml or json and may have contains basic stuff for your site:

auth.User:
  admin:
    password: root
    mail: root@admin.cz
web.Page:
  QuickStart:
    title: Quickstart
    slug: quickstart
    override_url: /
    featured: false
    theme: __first__
    in_navigation: true
    active: true
    color_scheme: __first__
    content:
      header:
        web.SiteHeadingWidget:
          attrs:
            site_title: Leonardo Site
            content_theme: navbar
            base_theme: default
          dimenssions:
            md: 2
        web.TreeNavigationWidget:
          attrs:
            depth: 2
            content_theme: navbar
            base_theme: default
          dimenssions:
            md: 6
        web.UserLoginWidget:
          attrs:
            inline: true
            type: 2
            content_theme: navbar
            base_theme: default
          dimenssions:
            md: 4
elephantblog.Entry:
  Test:
    title: Test
    slug: test
    author:
      type: auth.User
      pk: 1
    content:
      main:
        elephantblog.HtmlTextWidget:
          attrs:
            text: Hello world !
            content_theme: default
            base_theme: default
          dimenssions:
            md: 2

From local source

python manage.py bootstrap_site --name=demo.yaml

This mechanismus is really simple without any magic features. Just define your model entyties with some parameters. For FeinCMS models is there field called content which is dictionary of content regions like col3 with some Widgets.

From remote host

python manage.py bootstrap_site --url=http://raw.githubusercontent.com/django-leonardo/django-leonardo/develop/contrib/bootstrap/demo.yaml

Salt Installation

if your infrastucutre is managed by Salt use and contribute to our Salt Formula

https://github.com/django-leonardo/salt-formula-leonardo

Sample pillar

leonardo:
  server:
    enabled: true
    app:
      example_app:
            site_name: 'My awesome site'
        enabled: true
        development: true
        workers: 3
        bind:
          address: 0.0.0.0
          port: 9754
          protocol: tcp
        source:
          type: 'git'
          address: 'git@repo1.robotice.cz:python-apps/leonardo.git'
          rev: 'master'
        secret_key: 'y5m^_^ak6+5(f.m^_^ak6+5(f.m^_^ak6+5(f.'
        database:
          engine: 'postgresql'
          host: '127.0.0.1'
          name: 'leonardo'
          password: 'db-pwd'
          user: 'leonardo'
        mail:
          host: 'mail.domain.com'
          password: 'mail-pwd'
          user: 'mail-user'
        admins:
          mail@majklk.cz:
            name: majklk
          mail@newt.cz: {}
        managers:
          mail@majklk.cz:
            name: majklk
          mail@newt.cz:
            name: newt
        plugin:
          eshop: {}
          static: {}
          sentry: {}
          my_site:
            site: true
          blog:
            source:
              engine: 'git'
              address: 'git+https://github.com/django-leonardo/leonardo-module-blog.git#egg=leonardo_module_blog'
        languages:
          en:
            default: true
          cs: {}
          de: {}

Configuration

Leonardo Configuration

Leonardo is Django powered. All important settings is related with standard Django settings, but is there some leonardo specific configuration.

Configure files

  • local_settings in your PYTHONPATH for all stuff
  • or your_site/local/settings.py

Note

leonardo_site must be in the PYTHONPATH

SITE_ID = 1
SITE_NAME = 'leonardo'
# or full domain
SITE_DOMAIN = 'www.leonardo.cz'

LANGUAGE_CODE = 'en'

RAVEN_CONFIG = {}

Note

Leonardo finds and includes all modules which has descriptor(leonardo modules).

if you want you can specify your custom APPS:

APPS = [
    'blog',
    'eshop',
    'leonardo_site',  # our app
]

Leonardo template https://github.com/django-leonardo/site-template

manage.py makemigrations --noinput
manage.py migrate --noinput
manage.py sync_all

Change admin site name

SITE_HEADER = "Leonardo administration"

SITE_TITLE = "Leonardo site admin"

Apps, modules, themes ..

Leonardo has own specific app/module system. This system is same as Django, but provide some improvements, which makes time for installing and configuring new app shorter

APPS = ['leonardo']

# is same as

INSTALLED_APPS = ['leonardo']

But if configured via APPS, Leonardo tryies find default configuration in main descriptor of module. Descriptor may contains many various properties, which is safely merge into main settings. For full description see modules.

Leonardo

LEONARDO_MODULE_AUTO_INCLUDE = True

This option says please do not auto include leonardo modules.

LEONARDO_MEMOIZED = True

If set False is disabled any content cache.

For disable System Module which provide untested and unsecure features.

LEONARDO_SYSTEM_MODULE = True

Frontend Edit

LEONARDO_FRONTEND_EDITING = True

Horizon

Horizon has own urls finder, which provide capabilities for defining dashboards, panels.. in default state is included in main leonardo’s urls, but you can turn off, but you must map external app to any Page which provide horizon namespace.

HORIZON_ENABLED = False

Note

Before this, please add external app Horizon to any Page, because may broke admin.

Live configuration

Live configuration is based on django-constance with few improvements.

  • basic grouping via CONSTANCE_CONFIG_GROUPS which makes tabs for django admin
  • access to config keys from standard django settings
  • really live settings, set every value to django settings and respect the default value from them
class Default(object):

    optgroup = 'GA'

    apps = [
        'analytical',
        'leonardo_module_analytics',
    ]

    config = {
        'GOOGLE_ANALYTICS_PROPERTY_ID': ('xx-xxx-x', _('Google Site identificator')),
        'GOOGLE_ANALYTICS_SITE_SPEED': (False, _('analyze page speed')),
        'GOOGLE_ANALYTICS_ANONYMIZE_IP': (False, _('anonymize ip')),
    }

default = Default()

Note

Please be sure about keys in config, all is merged into one big dictionary which is used. Last wins.

All config keys will be namespaced under GA and available from standard django settings:

from django.conf import settings

settings.GOOGLE_ANALYTICS_PROPERTY_ID
-> xx-xxx-x

Warning

This may not work on some special environments like a sqlite. For stable usage in modules we recommend using via constance see below.

Backends

In default state is used database backend but redis is recommended.

Redis

installing via leonardo extras

pip install django-leonardo[redis]

set your configuration

APPS = ['constance.backends.redisd']

CONSTANCE_BACKEND = 'constance.backends.redisd.RedisBackend'

CONSTANCE_REDIS_CONNECTION = {
    'host': 'localhost',
    'port': 6379,
    'db': 0,
}

optionaly

CONSTANCE_REDIS_PREFIX = 'constance:myproject:'

for more configuration choices visit http://django-constance.readthedocs.org/en/latest/backends.html

Using in your code

from django.conf import settings

if settings.THE_ANSWER == 42:
    answer_the_question()

of via constance module

from constance import config

if config.THE_ANSWER == 42:
    answer_the_question()

Storage / Media

In this time we have good integration of Django-Filer, which provides good base for us.

We support for standard scenarious:

  • make, upload, delete, move folders and files
  • import files (scan) into concrete folder via admin or command
  • basic media entities

Configuation

put your configuation into your local_settings.py, these is defaults, you can also update only one concrete field, but you must import default from leonardo.module.media.settings

FILER_ENABLE_PERMISSIONS = True

FILER_STORAGES = {
    'public': {
        'main': {
            'ENGINE': 'filer.storage.PublicFileSystemStorage',
            'OPTIONS': {
                'location': '/path/to/media/filer',
                'base_url': '/smedia/filer/',
            },
            'UPLOAD_TO': 'filer.utils.generate_filename.randomized',
            'UPLOAD_TO_PREFIX': 'filer_public',
        },
        'thumbnails': {
            'ENGINE': 'filer.storage.PublicFileSystemStorage',
            'OPTIONS': {
                'location': '/path/to/media/filer_thumbnails',
                'base_url': '/smedia/filer_thumbnails/',
            },
        },
    },
    'private': {
        'main': {
            'ENGINE': 'filer.storage.PrivateFileSystemStorage',
            'OPTIONS': {
                'location': '/path/to/smedia/filer',
                'base_url': '/smedia/filer/',
            },
            'UPLOAD_TO': 'filer.utils.generate_filename.randomized',
            'UPLOAD_TO_PREFIX': 'filer_public',
        },
        'thumbnails': {
            'ENGINE': 'filer.storage.PrivateFileSystemStorage',
            'OPTIONS': {
                'location': '/path/to/smedia/filer_thumbnails',
                'base_url': '/smedia/filer_thumbnails/',
            },
        },
    },
}

Imports

manage.py import_files --path=/tmp/assets/images
manage.py import_files --path=/tmp/assets/news --folder=images

Note

via admin we support only relative(MEDIA_ROOT) scan

Migrations

Leonardo itself does not come with any migrations. It does not have to: Its core models haven’t changed for several versions now. This does not mean migrations aren’t supported. You are free to use either Django’s builtin migrations support, or also South if you’re stuck with Django versions older than 1.7.

Django’s builtin migrations

  • Create a new folder in your app with an empty __init__.py inside.

  • Add the following configuration to your settings.py:

    MIGRATION_MODULES = {
        'web': 'leonardo_site.migrations',
    }
    
python manage.py makemigrations --noinput

python manage.py migrate --noinput

If you have database already created, redirect your migration and create empty migrations

add this to your settings.py

MIGRATION_MODULES = {
    'web': 'leonardo_site.migrations',
}

create empty migrations to new path

python manage.py makemigrations --empty web

For big apps we recommend separation of migrations per module, like this:

MIGRATION_MODULES = {
    'web': 'leonardo_site.migrations.web',
}

If you changed LANGUAGES Django check new migrations, which changed choices on translation of media models. For these purposes we recommend redirect affected apps:

MIGRATION_MODULES = {
    'web': 'leonardo_site.migrations.web',
    'media': 'leonardo_site.migrations.media',
}

Note

Don’t forget to create corresponding directories.

You can also redirect migrations from any leonardo module. Just use MIGRATIONS_MODULES in the module descriptor something like this:

LEONARDO_MIGRATION_MODULES = {
    'web': 'my_module.migrations.web',
    'media': 'my_module.migrations.media',
}

With this, leonardo supports changing default location leonardo_site as project module.

Languages

For settings Langugages follow standard Django settings like this:

LANGUAGE_CODE = 'en'

LANGUAGES = (
    ('en', 'EN'),
    ('cs', 'CS'),
)

This is default settings which specify English as default Language

Note

location of these settings is local_settings.py or your Site settings file

for switching to Czech as default redefine:

LANGUAGE_CODE = 'cs'

LANGUAGES = (
    ('cs', 'CS'),
    ('en', 'EN'),
)

Warning

Ordering in the LANGUAGES is important for translations ! First must be default language.

Themes, Templates and Color variations

Sync Themes

Sync widget themes

python manage.py sync_all

replace db from files (new version of core template etc..)

python manage.py sync_all -f

command by command

Run collectstatic

python manage.py collectstatic --noinput

After collectstatic create page themes

python manage.py sync_page_themes

load widget themes

python manage.py sync_widget_themes

Extend

New Site

Easiest way how you can create new Leonardo Site is our Django Site Template which lives here

https://github.com/django-leonardo/site-template

If you have installed Leonardo simply type

django-admin startproject --template=https://github.com/django-leonardo/site-template/archive/master.zip myproject

cd myproject
  • local_settings in your PYTHONPATH for all stuff
  • or settings/menu .. in conf

Note

leonardo_site must be in the PYTHONPATH

SITE_ID = 1
SITE_NAME = 'leonardo'
# or full domain
SITE_DOMAIN = 'www.leonardo.cz'

LANGUAGE_CODE = 'en'

RAVEN_CONFIG = {}

APPS = [
    'blog',
    'leonardo_site',  # our app
]

Leonardo template https://github.com/django-leonardo/site-template

If you have configured your database and other common stuff run

manage.py makemigrations --noinput
manage.py migrate --noinput
manage.py sync_all

New Theme

Best example is live code, we have two base themes for you and lives under main github group

As you can see theme must contains one template for page layout and optionaly base css for this layout and some color variations lives in skins directory.

Directory structure:

leonardo_theme_bootswatch
    |-- __init__.py
    |-- templates
        |-- base
            |-- page
                |-- bootswatch.html
    |-- static
        |-- themes
            |-- bootswatch
                |-- _variables.scss
                |-- cosmo
                    |-- _variables.scss
                    |-- _styles.scss
                    |-- scheme.scss

Required stuff for color sheme is scheme.scss which may contains something like this:

@import "_variables";
@import "../_styles";
@import "_styles";

Warning

Every skin must have _variables file which is dynamically appended to every widget scss file.

If we run

python manage.py sync_all

or any his variations Leonardo load base page templates into database and after this step tries find css in theme location. After that we have ready theme for our pages and also for editing via admin interface.

Leonardo automatically load these tested themes if is present. For their installation write this in your environment

For all supported themes simple do

pip install django-leonardo[themes]

python manage.py sync_all -f

sync themes

python manage.py sync_all -f

Solo AdminLTE

pip install leonardo_theme_adminlte

# or via main package

pip install django-leonardo[adminlte]

Note

Don’t remmeber sync themes, which is described in the web/themes

For new theme is situation more complex. You have two options:

  • create your Leonardo module descriptor and put your theme name into main APPS
  • pur your theme name directly into main INSTALLED_APPS, but this is more hard way

For first option you must write simple leonardo module descriptor in my_new_theme_name/__init__.py

class Default(object):

    # define your specific apps
    apps = ['my_new_theme_name']

default = Default()

and add it to APPS in local_settings.py

APPS = [
    'my_new_theme_name'
]

That’s it. Run sync_all.

Leonardo Module

Leonardo module is standard Django application with many additional posibilities. In Leonardo module just type your needs and develop your application.

start module directory structure:

leonardo_module_blog
    |-- __init__.py
    |-- settings.py

Application

As Django documentations says, you can define your apps in apps.py or anywhere, in Leonardo we use __init__.py for simplicity. But you can define it where you want.

Redirect configurations to any other location, like:

# Django stuff
default_app_config = 'leonardo_module_blog.apps.BlogConfig'
leonardo_module_conf = 'leonardo_module_blog.apps'

Required setup for Django Application are defined AppConfig which has basic attributes described in Django Documentation. For Leonardo Module are required one options with prefix LEONARDO or default attribute which specify Leonardo Module stuff.

from django.apps import AppConfig

default_app_config = 'leonardo_module_blog.BlogConfig'

class Default(object):

    optgroup = 'Blog'

    apps = [
        'leonardo_module_blog',
        'elephantblog',
        'leonardo_module_analytics',
    ]

    js_files = [
        'js/redactor.js'
    ]

    css_files = [
        'css/redactor.css'
    ]

    config = {
        'BLOG_PAGINATE_BY': (10, _('Blog Entries Pagination')),
        'DISQUS_COMMENTS': (False, _('Enable Disqus comments')),
        'DISQUS_SHORTNAME': ('michaelkuty', _('Disqus shortname identificator.')),

    }

    navigation_extensions = [
        'elephantblog.navigation_extensions.treeinfo',
    ]

    absolute_url_overrides = {
        'elephantblog.entry': 'leonardo_store.overrides.elephantblog_entry_url_app',
        'elephantblog.categorytranslation':
        'leonardo_store.overrides.elephantblog_categorytranslation_url_app',
    }


# standard django Application
class BlogConfig(AppConfig, Default):
    name = 'leonardo_module_blog'
    verbose_name = ("Blog")

default = Default()  # define module configuration

That’s all.. Leonardo go throught every module defined in your APPS and merge all items to main settings file. Complete reference you can see below.

Note

Leonardo supports two syntax. One Pythonic way which is described upstair. default attribute which respect simple Python Object.

For some users are Python way unnecessarily complicated, for this people leonardo supports another config syntax:

LEONARDO_APPS = ['app1']

LEONARDO_ABSOLUTE_URL_OVERRIDES = {
    'elephantblog.entry': 'leonardo_store.overrides.elephantblog_entry_url_app',
    'elephantblog.categorytranslation':
    'leonardo_store.overrides.elephantblog_categorytranslation_url_app',
}

Note

Just use same keys with prefix and uppercase LEONARDO_

Tip

For all possibility settings keys see Module Reference

Settings

in the settings you may have something like this

BLOG_TITLE = 'name'

# whatever

As you expext every key from settings will be inported and merged into main settings file.

Warning

Be careful if you declare keys in the module/settings.py. Every key is imported without special merging process which may override your global settings ! It was designed only for module/app specific defaults.

Release

For releasing big amount of pip packages we use PBR which was developed for OpenStack and we have tunned version which lives here https://github.com/michaelkuty/pbr.

PBR can and does do a bunch of things for you:

  • Version: Manage version number based on git revisions and tags
  • AUTHORS: Generate AUTHORS file from git log
  • ChangeLog: Generate ChangeLog from git log
  • Sphinx Autodoc: Generate autodoc stub files for your whole module
  • Requirements: Store your dependencies in a pip requirements file (install from vcs)
  • long_description: Use your README file as a long_description
  • Smart find_packages: Smartly find packages under your root package

With this tool is managing python module pretty simple. Add these lines to your setup.py:

import setuptools

# In python < 2.7.4, a lazy loading of package `pbr` will break
# setuptools if some other modules registered functions in `atexit`.
# solution from: http://bugs.python.org/issue15881#msg170215
try:
    import multiprocessing  # noqa
except ImportError:
    pass

setuptools.setup(
    setup_requires=['pbr'],
    pbr=True)

and write meta to setup.cfg

[metadata]
name = leonardo-team
summary = Team Application for Leonardo CMS or plain FeinCMS
description-file =
    README.rst
author = Michael Kuty
author-email = kutymichael@gmail.com
home-page = https://github.com/leonardo-modules/leonardo-team.git
classifier =
    Development Status :: 5 - Production/Stable
    Framework :: Django
    Intended Audience :: Developers
    License :: OSI Approved :: BSD License
    Operating System :: OS Independent
    Programming Language :: Python
    Programming Language :: Python :: 2.6
    Programming Language :: Python :: 2.7
    Programming Language :: Python :: 3
    Programming Language :: Python :: 3.3
    Programming Language :: Python :: 3.4
    Topic :: Software Development
    Topic :: Software Development :: Libraries :: Application Frameworks

[files]
packages =
    team

and run

python setup.py sdist register

PBR is GIT driven if you want add new version for release just create new tag like:

git tag v1.4

and then upload new release to pip:

python setup.py sdist upload

Note

Full documnetation of PBR lives there http://docs.openstack.org/developer/pbr/

Leonardo Descriptor Reference

Descriptor is Leonardo specific and is inspired from Openstack Horizon where is used for non invasive extend Dashboard extends. In the Leonardo we use same pattern, but with some additions.

Directory structure:

my_awesome_module
    |-- __init__.py
    |-- settings.py
    |-- urls.py

Warning

Leonardo include all settings and urls in root of module.

Descriptor reference

Leonardo

apps - leonardo modules or whatever:

apps = [
    'leonardo_module_blog',
    'elephantblog',
    'leonardo_module_analytics',
]

urls_conf url path to include

public if is set to True Leonardo does not decorate included url patters for required authentification

module_actions array of templates included in the frontend side bar

FeinCMS

widgets - FeinCMS widgets:

widgets = [
        BlogCategoriesWidget,
        RecentBlogPostsWidget,
    ]

optgroup - menu group name for widgets:

optgroup = 'Blog'

plugins - FeinCMS 3rd party apps support:

plugins = [
    ('elephantblog.urls', 'Blog entries'),
]

page_extensions - FeinCMS page extensions

navigation_extensions - FeinCMS Page Extensions - will be imported before reofistering for proper load:

navigation_extensions = [
    'elephantblog.navigation_extensions.treeinfo',
]
Horizon

js_files - merged and added to main page header:

js_files = [
    'js/redactor.js'
]

css_files linked in head as style:

css_files = [
    'css/redactor.css'
]

scss_files linked in head as scss style:

scss_files = [
    'scss/redactor.scss'
]

angular_modules Angular modules which will be loaded:

angular_modules = [
    'angular-carousel'
]

js_spec_files - Angular specific see https://github.com/openstack/horizon/blob/master/openstack_dashboard/enabled/_10_project.py#L44

Constance

config - dictionary of keys for django-constance:

config = {
    'BLOG_PAGINATE_BY': (10, _('Blog Entries Pagination')),
    'DISQUS_COMMENTS': (False, _('Enable Disqus comments')),
    'DISQUS_SHORTNAME': ('michaelkuty', _('Disqus shortname identificator.')),

}
Django

auth_backends - AUTHENTICATION_BACKENDS:

auth_backends = [
    'oscar.apps.customer.auth_backends.EmailBackend'
]

context_processors - Django Context Processors:

context_processors = [
    ...
    'oscar.apps.checkout.context_processors.checkout',
    'oscar.apps.customer.notifications.context_processors.notifications',
    ...
]

middlewares - Django Middlewares:

middlewares = [
    'oscar.apps.basket.middleware.BasketMiddleware',
]

migration_modules - allow override migration’s location:

migration_modules = {
    'elephantblog': 'leonardo_module_blog.migrations',
}

absolute_url_overrides - model name and method wich would be imported for easy integrating 3rd party app:

absolute_url_overrides = {
    'elephantblog.entry': 'leonardo_store.overrides.elephantblog_entry_url_app',
}

Minimal example

your app directory structure:

leonardo_module_blog
    |-- __init__.py
    |-- settings.py
__init__.py

As Django documentations says, you can define your apps in apps.py or anywhere, in Leonardo we use __init__.py for simplicity. But you can define it where you want.

from django.apps import AppConfig

default_app_config = 'leonardo_module_blog.BlogConfig'

class Default(object):

    optgroup = 'Blog'

    apps = [
        'leonardo_module_blog',
        'elephantblog',
        'leonardo_module_analytics',
    ]

    js_files = [
        'js/redactor.js'
    ]

    css_files = [
        'css/redactor.css'
    ]

    config = {
        'BLOG_PAGINATE_BY': (10, _('Blog Entries Pagination')),
        'DISQUS_COMMENTS': (False, _('Enable Disqus comments')),
        'DISQUS_SHORTNAME': ('michaelkuty', _('Disqus shortname identificator.')),

    }

    navigation_extensions = [
        'elephantblog.navigation_extensions.treeinfo',
    ]

    absolute_url_overrides = {
        'elephantblog.entry': 'leonardo_store.overrides.elephantblog_entry_url_app',
        'elephantblog.categorytranslation':
        'leonardo_store.overrides.elephantblog_categorytranslation_url_app',
    }


# standard django Application
class BlogConfig(AppConfig, Default):
    name = 'leonardo_module_blog'
    verbose_name = ("Blog")

default = Default()  # inicialize

That’s all.. Leonardo go throught every module defined in your APPS and merge all items to main settings file. Complete reference you can see below.

settings.py

in the settings you may have something like this

BLOG_TITLE = 'name'

# whatever

As you expext every key from settings will be inported and merged into main settings file.

Warning

Be careful if you declare keys in the module/settings.py. Every key is imported without special merging process which may override your global settings ! It was designed only for module/app specific defaults.

Components

Thumbnails

Sometimes is hard to manage all templates to use one thumbnail tag.

Leonardo has one thumbnail tag which lives under thumbnail templatetags.

This template tag combine sorl and easy-thumbnails. This templatetag can render different thumbnails in same template without syntax error.

this makes big advantages with supporting more backends for example we use easy_thumbnails and if we install leonardo_module_eshop or plain Django oscar which requires sorl-thumbnail as default thumbnail library we have problem. Leonardo support many variations thumbnail tags

Sorl

{% load thumbnail %}
{% thumbnail widget.image.file size format="PNG" as thumb %}
    <img src='{{ thumb.url }}' alt='my-image' />
{% endthumbnail %}

Easy-thumbnails

{% load thumbnail %}
<img src="{% thumbnail profile.photo 50x50 crop %}" alt="" />

Combined

{% load thumbnail %}

{% thumbnail widget.image.file size format="PNG" as thumb %}
    <img src='{{ thumb.url }}' alt='my-image' />
{% endthumbnail %}

<img src="{% thumbnail profile.photo 50x50 crop %}" alt="" />

For more examples and settings must follow appropriate pages. For Sorl http://sorl-thumbnail.readthedocs.org/en/latest/index.html and for Easy-Thumbnails https://github.com/SmileyChris/easy-thumbnails

Live configuration

Live configuration is based on django-constance with few improvements.

  • basic grouping via CONSTANCE_CONFIG_GROUPS which makes tabs for django admin
  • access to config keys from standard django settings
  • really live settings, set every value to django settings and respect the default value from them

Live settings now supports these types:

  • String
  • Number
  • Boolean
class Default(object):

    optgroup = 'GA'

    apps = [
        'analytical',
        'leonardo_module_analytics',
    ]

    config = {
        'GOOGLE_ANALYTICS_PROPERTY_ID': ('xx-xxx-x', _('Google Site identificator')),
        'GOOGLE_ANALYTICS_SITE_SPEED': (False, _('analyze page speed')),
        'GOOGLE_ANALYTICS_ANONYMIZE_IP': (False, _('anonymize ip')),
    }

default = Default()

Note

Please be sure about keys in config, all is merged into one big dictionary which is used. Last wins.

All config keys will be namespaced under GA and available from standard django settings:

from django.conf import settings

settings.GOOGLE_ANALYTICS_PROPERTY_ID
-> xx-xxx-x

Warning

This may not work on some special environments like a sqlite. For stable usage in modules we recommend using via constance see below.

Using in your code

from django.conf import settings

if settings.THE_ANSWER == 42:
    answer_the_question()

of via constance module

from constance import config

if config.THE_ANSWER == 42:
    answer_the_question()

Messagess

Drop-in replacement for django.contrib.messages which handles Leonardo’s messaging needs (e.g. Celery tasks, AJAX communication, etc.).

for async messages install

pip install django-async-messages

from https://github.com/codeinthehole/django-async-messages

>>> from leonardo import messages
>>> barry = User.objects.get(username='barry')
>>> messages.debug(barry, "Barry was here")
>>> messages.info(barry, "Hi, Barry")
>>> messages.success(barry, "Barry, your report is ready")
>>> messages.warning(barry, "Barry, you didn't lock your session")
>>> messages.error(barry, "You are not Barry")

or standard request

>>> messages.error(request, "You are not Barry")

Modals Dialogs

Standard modals via views

modal_size - valid options md, lg, sm

from horizon import forms

class WidgetDeleteView(forms.ModalFormView):

    form_class = WidgetDeleteForm

        template_name = 'leonardo/common/modal.html'

    def get_context_data(self, **kwargs):
        context = super(WidgetDeleteView, self).get_context_data(**kwargs)

        context['url'] = self.request.build_absolute_uri()
        context['form_action'] = 'POST'
        context['modal_header'] = _('Create new Moon')
        context['title'] = _('Create new Moon')
        context['form_submit'] = _('Create')
        context['heading'] = self.get_header()
        context['help_text'] = _('Your awesome help text')
        context['modal_size'] = 'lg'

        return context

Lightboxes

For galleries you can use default Lightboxes for Bootstrap 3 see example below:

<a class="thumbnail" data-toggle="lightbox" data-title="{{ image.caption }}" data-footer="{{ image.description }}" href="{{ image.url }}">
  {% thumbnail file.file "320x200" crop="center" as thumbnail %}
  <img class="img-responsive" src="{{ thumbnail.url }}" alt="{{ category_file.default_alt_text }}" />
  {% endthumbnail %}
</a>

Contribute

Feel free and contribute to Leonardo CMS ! Follow next steps

Contributors

How to contribute?

  • Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
  • Fork https://github.com/django-leonardo/django-leonardo.git on GitHub to start making your changes to the develop branch.
  • Write a test which shows that the bug was fixed or that the feature works as expected.
  • Make sure to add yourself to the contributors.
  • Send a pull request

You can help further the development of django-leonardo by reporting bugs, submitting documentation, patches, with monetary or hardware donations.

Lead developers

Contributors (in alphabetical order)

  • No contribution