Ella CMS

Ella is a Content Management System based on Python web framework Django with a main focus on high-traffic news websites and Internet magazines.

It is composed from several modules:

  • Ella core is the main module which links the rest together. It defines architecture on which other modules are built but doesn’t do anything really usefull all alone.
  • Ella core plugins are plugins that are shipped in one package together with Ella. There are articles and positions which we consider to be a basic toolbox for each Ella site.
  • Other Ella plugins are standalone applications (and therfore not shipped with the core) that provide some specific functionality using Ella’s architecture. We can mention polls, galleries, quizes and many more.

Feature highlights:

  • Simple organization of content based on categories
  • Efficent implementation of the published content
  • In-build photo formating backend
  • Django-admin ready
  • Plugin system
  • Flexibile
  • Scalable
  • Extensible
  • Caching-friendly
  • Well tested
  • Proven in production environment

For creating site using Ella, working knowledge of Django and its templating language is required. It is therfore highly recommended to get familiar with Django before you try to dwell into Ella. You can start in Django documentation.

Documentation

Contents:

Quickstart

Setting up Ella

This tutorial will guide you through the process of creating and deploying your first Ella-based site. Since Ella is a CMS, we will create a blog. This first step will take us through setting up our environment, installing all the dependencies and kicking off the project. Before you dive into it, we suggest you go through the official Django tutorial to get yourself familiar with Django since we will be relying on that.

Dependencies

We assume that python, setuptools and python-imaging (PIL) are installed on your system directly since they can be non-trivial to install the python way. We will be working with pip and virtualenv which are great tools for any Python project.

Note

We will not cover any version control, but we strongly advise you use some (we prefer GIT) to keep track of your emerging project.

First we need to install virtualenv (under root):

easy_install virtualenv

Now we can create and activate a virtualenv where our project and all related code will reside:

virtualenv ella_sandbox
source ella_sandbox/bin/activate

Next, install Ella into your fresh virtualenv. Ella has all it’s dependencies covered in it’s setup, so it’s fairly sufficent to run following command using pip:

pip install ella

After these steps, everything required is ready and we can create a new Django project using Ella in standard Django way:

mkdir ellablog
cd ellablog
django-admin.py startproject ellablog
settings.py

Our first step in actual code will be adding Ella to your project’s INSTALLED_APPS along with some required settings, the resulting values (unchanged values are omitted) should look similar to this:

...
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.redirects',

    'ella.core',
    'ella.photos',
    'ella.articles',
)
...

In order to create our new blog, we are gonna need some HTML templates showing post listings, post details, hompage etc. Therefore, we have to tell Django, where to look for those templates. This settings are kept in settings.py contained in root of our project. Second, we need to specify a directory + URL used to serve media files from:

Note

Media files and static files are not the very same thing. We consider media files those, that are uploaded dynamically by users, e.g. main article photos. On the other hand, static files usually don’t change and their common representatives include CSS styleseets, JS sources etc.

from os.path import join, dirname

PROJECT_ROOT = dirname(__file__)

# set the templates directory
TEMPLATE_DIRS = (
    join(PROJECT_ROOT, 'templates'),
)

# give Django media settings
MEDIA_ROOT = join(PROJECT_ROOT, 'media')
MEDIA_URL = '/media/'

This will tell Django, that it should look for the templates in directory templates which is located in the root of the project directory. And store the media in PROJECT_ROOT/media/ directory.

urls.py

Last thing to configure is the URL mappings. We want to include ella.core.urls which handle all the Ella magic but also create some mappings that will serve our static files (and static files for admin) in the development server. Note that these patterns for static files will work only when DEBUG mode is turned on since it would be rather inefficent in production (for more on this topic, see Managing static files section in Django docs). In similar fashion, serve also media files discussed in previous paragraph:

from django.conf.urls.defaults import *
from django.conf import settings
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns

# make sure to import ella error handlers
from ella.core.urls import handler404, handler500

# register apps for Django admin and let the apps do any initialization they need
from ella.utils.installedapps import call_modules
call_modules(('admin', 'register', ))

urlpatterns = patterns('',)

# actual URL mappings
urlpatterns += patterns('',
    # serve media files
    (r'^%s/(?P<path>.*)$' % settings.MEDIA_URL, 'django.views.static.serve', { 'document_root': settings.MEDIA_ROOT, 'show_indexes': True }),

    # run Django admin
    (r'^admin/', include(admin.site.urls)),

    # enable Ella
    (r'^', include('ella.core.urls')),
) + staticfiles_urlpatterns()

Note

Instead of calling admin.autodiscover we are using Ella’s call_modules which is a more generic version of the same thing. and allows us to load additional modules - namely register.py where, by convention all Ella apps put the codethey need for their initialization (connecting signal handlers, registering custom urls etc.)

Database

Last configuration step is the database settings. Ella supports all Django DB backends. Example configuration for MySQL can look like this:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'ellablog',
        'USER': 'user',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
    }
}

To make this work, you should make appropriate database by your custom DB-related tool (such as command-line tool mysql in Linux-like operating systems). After creating the database, you can proceed with creating all the required tables by Django for you (and admin user during this process):

python manage.py syncdb

Congratulations, you should have a working Ella project. If you start the development server by typing:

python manage.py runserver

try to load the site’s root. If everything worked out, you should get a welcome screen looking similar to this:

_images/welcomescreen.png

Now when we have a working project, we need to actually create the site in the admin interface. To use it, go to /admin/ and log in using credentials you entered when creating your superuser account during the syncdb command. While being there we will also create an article - our very first blog post so that we can actually have something to work with in our templates in the next step of the tutorial.

Note

If you are impatient to start, just play around with the admin to create one instance of ella.core.models.Category to get the root of the web working and then one ella.articles.models.Article published in that category (you might need to create additional model like Author on the way).

First we need some theory on how Ella handles sites, categories and published objects though.

Ella sites and categories

Ella was designed to serve several sites from a single database. It does so by using Django’s built-in sites framework. The sites app creates a default Site called example.com during the syncdb command. Just rename the domain name to relevant value and you will have an Ella site, just empty.

Within sites, Ella organizes content into categories. Categories (instances of ella.core.models.Category) are organized in a tree for each site. Every site needs to have exactly one what we call root category - a category without a parent. This category then represents the root of the website (/).

Categories are represented by their tree_path - a path of slugs from root category, for example with categories layout:

Ella Blog
    About
    Technology
        Concepts
        Django
            Django applications
        Typical deployment env

the tree_path values would be:

Category tree_path attribute
Ella Blog  
About about
Technology technology
Concepts technology/concepts
Django technology/django
Django applications technology/django/django-applications
Typical deployment env technology/typical-deployment-env

Category‘s URL is it’s tree_path (which is what makes the root category the root of the site) and every post in Ella belongs to one or more categories, nothing shall exist outside of the category tree.

Publishable object

The main objective of Ella is publishing content. Ella together with it’s plugins provides several types of content (Article, Gallery, Quiz, ...) and can be easily extended to add more (just define the model) or used with existing models.

For ease of manipulation and efficiency, all content models inherit from ella.core.models.Publishable. This base class has all the fields needed to display a listing of the content object (title, description, slug, photo), basic metadata (category, authors, source). When using Ella API you will always receive an instance of the actual class (Article) and not the base class (Publishable). If you have access to only a Publishable instance the target property will return instance of the correct class (it holds a reference to it’s ContentType).

Information about publication are also kept on the Publishable model (attributes published, publish_from, publish_to and static). All these information together are used to create an URL for the object which will point to it’s detail (e.g. article content). There are two types of publication with slightly different use cases:

  • time-based has URL containing the date of publishing and should be used for objects that have some relevance to date (most of the content presumably since Ella was designed to power magazines and news sites). The URL of an object published in time-based way will look like:

    /category/tree/path/[YEAR]/[MONTH]/[DAY]/[CONTENT_TYPE_NAME]/slug/
    

    so for example:

    /about/2007/08/11/ella-first-in-production/
    
  • static has no date in it’s URL and should be used for objects with universal validity. URL of statically published objects contains a primary key reference to avoid namespace clashes:

    /category/tree/path/[PK]-slug/
    

    for example:

    /about/1-ella-first-in-production/
    

Just setting up publish information for a Publishable object makes it visible (starting from publish_from) but doesn’t make it appear in any listing in any Category. For that you need to specify in which categories you want it listed.

Listing object

ella.core.models.Listing instances carry the information in which Category and when should be a publishable object listed - it enables users to list the object in as many categories as they wish at arbitrary times (but not sooner that the Publishable.publish_from).

By default, listings in the root category only contain Listings specifically targeted there whereas listings for any subcategory also contains all the listings of it’s subcategories. This is a model we found most useful when working with large sites where the site’s homepage needs to be controlled closely by editors and the interim categories only serve as aggregators of all the content published in them either directly or via a subcategory.

Creating a site

Now you should have enough information to be able to start exploring the admin (found on /admin/) and create your own site and it’s first post. You will know that you were succesfull if you manage to create and publish an article whose URL gives you a TemplateDoesNotExist exception upon accessing - that means we are ready to create some templates.

Basic templates

Now that we have some sample data to work with we can finally start creating the templates we need to get the site running.

Note

For more information on what templates Ella uses and what context is passed in, have a look at Template overview.

page/category.html

First we will create a template rendering a category: page/category.html. This is a default template that will be used for all categories if their specific template (one with their path) isn’t found. The two most important variables in the context we want to use is {{ category }} containing the Category model itself and {{ listings }} containing a list of Listing objects for that category ordered by publish_from and/or priority.

The basic template will look like:

<h1>Welcome to category {{ category.title }}</h1>
<p>{{ category.description }}</p>

{% for listing in listings %}
    <p>
        <a href="{{ listing.get_absolute_url }}">{{ listing.publishable.title }}</a>
        {{ listing.publishable.description|safe }}
    </p>
{% endfor %}

That will render the category title, description and a list of objects published in that category. Upon accessing / you should then see the name of the category and the article you created in administration.

page/listing.html

This template represents the archive, it gets the same context as page/category.html and the same code can be used. We will use the same code:

{% extends "page/category.html" %}
page/object.html

As with page/category.html, page/object.html is a fallback template that will be used for rendering any object if more suitable template isn’t found. In real life we will probably have different templates for different content types, but to verify the concept and get us started a simple template should be enough:

<h1>{{ object.title }}</h1>
<p>Published on {{ placement.publish_from|date }} in category: <a href="{{ category.get_absolute_url }}">{{ category }}</a></p>
{% render object.description %}

This template will have access to the actual Publishable subclass instance (Article in our case), as opposed to page/category.html and page/listing.html which only gets instance of Publishable by default.

Note the use of {% render %} templatetag that is used to render rich-text fields (which object.description is) thorought Ella.

Error pages

By importing handler404 and handler500 in our urls.py, we turned over the control of error pages to Ella. This means that we need to create two additional templates: page/404.html:

<h1>Oops, nothing here</h1>

and page/500.html:

<h1>If you see this, let us please know how you did it, thanks!</h1>

Now that we have a set of rudimentary templates, we can try doing something useful with them.

Enhancing templates

Since Ella is a regular Django application, even it’s templates are just plain Django templates. Therefore we just refer you to other sources and Common gotchas & FAQs section to learn more about the templating language and it’s best practices, we will try to focus just on Ella-specific parts.

Boxes

First change we will make is abstract the rendering of the object listing on category homepage and archive. To do this, Ella provides a Box for individual objects. It’s primary use is as a templatetag. Boxes can be rendered for objects accessible through a variable or through a database lookup:

{% box <box_name> for <object> %}{% endbox %}
    or
{% box <box_name> for <app.model> with <field> <value> %}{% endbox %}

What {% box %} does is a little more then fancy include - it retrieves the object, find the appropriate template and renders that with object-specific context. The context can be quite different for an Article or Photo gallery. Boxes are usually used throughout an Ella site to provide maximum flexibility in rendering objects and also for embedding objects into rich text fields stored in the database (in text of an article for example). Some applications ( Defining positions on the page for example) also use boxes to represent objects.

To create our first box, we just need to create a template called box/listing.html containing:

<p>
    <a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
    {% render object.description %}
</p>

And change page/category.html to use the box instead of manually specifying the output:

<h1>Welcome to category {{ category.title }}</h1>
<p>{{ category.description }}</p>

{% for listing in listings %}
    {% box listing for listing %}{% endbox %}
{% endfor %}

If you still struggle, why the bloody Box is used instead of standard {% include SOMETHING %}, keep in mind following advantages:

  • They know which template to use with proper fallback engine.

  • The provide class-specific context so that an Article can have

    different context than Photo.

Template fallback mechanisms

In last step we created a few templates that should suffice for an entire site based on Ella. In real life you probably wouldn’t want every category and every object to share the same template. Ella provides a simple mechanism to target your templates more directly.

Let’s say that we want to create a specific template for rendering articles, just create a template called page/content_type/articles.article/object.html and you are done - next time you visit some article’s URL, this template will get rendered instead of your page/object.html. This template would be a good place to render the text of an article for example:

{% extends "page/object.html" %}
{% block content %}
    {% render object.content %}
{% endblock %}

Now if you just define the appropriate block in your page/object.html:

<h1>{{ object.title }}</h1>
<p>Published on {{ object.publish_from|date }} in category: <a href="{{ category.get_absolute_url }}">{{ category }}</a></p>
{% render object.description %}

{% block content %}{% endblock %}

You should be able to see your article’s text on the web.

Another way you can override your templates is based on Category. For example if you want to create a custom template for your root category (and your root category’s slug is ella-blog), just create one called page/category/ella-blog/category.html:

<h1>Welcome to site {{ category.site }}</h1>
<p>{{ category.description }}</p>

{% for listing in listings %}
    {% box listing for listing %}{% endbox %}
{% endfor %}

You will be greeted into the site and not your root category next time you visit the root of your blog. Just create any subcategory to check it will remain unaffected.

You can use the same simple mechanism (creating new templates) to change the look of your boxes for individual objects as well.

Note

For more detailed explanation of all the possible template names, have a look at Views, Template overview and Templatetags documentation.

I am a Python developer - where do I begin?

Developers have always a hard time. Unfortunately, you need to be familiar with the most topics this documentation offers. You will be the key person coders go to get advice from a you simply have to know the answers. This docs are trying to offer you a helping hand and cover most of the topics you will run into when working with Ella. Not only it gives you API reference, it also offers cooking recipes for common headache situations.

In this documentation, we expect you have proficency in these areas:

  • Standard Python programming.
  • At least rudimentary knowledge in Django:
    • Models
    • Views
    • Templating
    • Middleware
    • Context processors
    • Media/static handling
  • Knowledge of web programming paradigms: request, response, ....

Ella always tries to keep things as simple as possible, but, as you probably learned already, it’s not always just possible. If you want get familiar with Ella, it’s always best to start off with Quickstart to get a grasp of core concepts Ella is built upon. Next, continue with reading the docs in Features section. It is recommended to get yourself familiar with all the topics covered in here even though the Caching and Deployment are probably kinda advanced and not strictly required in the first run. If everything goes allright and you don’t feel lost, keep reading in the Common gotchas & FAQs section which covers frequent show-stoppers for new Ella users. Finally, do a fast sprint throught API reference and List of configuration settings to make your overview complete. And, of course - have a look at the code itself. Sometimes it works even better than best docs.

If feeling confused or not happy with the way Ella is presented or just simply having a note, please feel free to let us know! It is always appreciated.

I am a HTML coder - where do I begin?

As the creator of the templates, you should first get confident when working with Django templates. You should have good understanding of following concepts:

  • What are templates and how they are defined.
  • How to work with variables.
  • How to use use filters and what they are for.
  • What are Django templatetags and how to use them.
  • Basic overview of the in-built Django templatetags.
  • Template inheritance.

It would also be very handy, if you had basic understanding of how Django itself works, what are views and models. If you are familiar with all the mentioned topics, great! You are ready to jump into Ella right now. Here is a topic walkthrough we think will be ideal for you.

First, start with Quickstart. It covers the core principles in brief detail and should get you in touch with Ella basics. You will get familiar with basic Ella project setup, the most common templates, and the important models Ella is all about. As a next step, go to Features section and try to consume as much as possible from these topics:

If all those topics make sense, you will probably feel at home when going to Common gotchas & FAQs section, which will hopefully give you last remaining pieces of the puzzle. If still having questions, please contact us and give us the very welcomed feedback what is missing in the docs!

Features

This section presents core Ella features. It’s recommended to all of the user base. Some parts might be a little heavy for html coders though. It goes from the most basic aspects to the more and more specialized ones. It tries to show the concepts by examples and doesn’t go into the deep descriptions of the API. If you are looking for reference instead, go to the API reference.

As a rule of the thumb, all sections up to the Working with related objects are recommended for an every-day Ella user.

Template fallback mechanisms

Templates for rendering categories and publishable objects use fallback mechanism to provide you full control over what is rendered with minimum effort required.

When selecting template to use for given URL, Ella does several things based on what we are dealing with. However, in both cases there is final fallback to default templates which are:

  • category.html for a category template
  • object.html for an object template

Note

Under some circumstances, category.html can be overriden, see Defining custom template for category for more details.

Selecting template for a category

When selecting templates for a rendering of category, Ella uses this set of rules:

  1. Look at the tree_path. Try to find a template in page/category/[TREE_PATH]/category.html.

    Example:

    http://www.example.com/category/subcategory/

    Ella would first try to find page/category/category/subcategory/category.html.

  2. If template was not found in previous step, try to find the template of an direct ancestor providing we have one and it’s not the root category.

    Examples:

    http://www.example.com/category/subcategory/subsubcategory/

    First, try page/category/category/subcategory/category.html, next try page/category/category/category.html and stop because the category "category" is the main one.

    http://www.example.com/category/subcategory/

    Try page/category/category/category.html and stop.

    http://www.example.com/category/

    Would not try anything, we are already in main category.

    http://www.example.com/

    Would not try anything, we are in root.

  3. If template wasn’t found yet, use default template (which is page/category.html in most cases).

Selecting template for an object

Selecting template for an object adds even more possibilities for the developer. It also uses content_type in form of app_label.model_label (both lowercased). Example would be: articles.article, videos.video and so on.

  1. Try to find a template in page/category/[TREE_PATH]/content_type/[CONTENT_TYPE]/object.html.
  2. Try to find a template in page/content_type/[CONTENT_TYPE]/object.html.
  3. Continue in the same way as when selecting a category template except for using object.html instead of category.html.
Selecting template for a box

Lookup for boxes is done in "box" subdirectory. It then works exactly the same as for objects, except that the template name is the name of the box being rendered and last resort is template box/box.html.

Category detail page

Category detail is the very main page of most content websites. In Ella, we do not make any difference for homepages and other categories except for the different URLs. Ella uses categories in several ways:

  • For showing your homepage
  • For listing content that is published in the category
  • As a static page, e.g. for your contact page

Last use case scenario might be little awkward, but the design decision was made to make this as easy as possible. Because main focus of Ella is content-rich websites and online news, static pages are usually not the primary focus of an Ella project. It’s still quite simple to create personal websites though.

Working with category templates

When creating category templates, here are some basic rules you can count on:

The template used is by default category.html using the template fallback mechanism (for details on that, see Template fallback mechanisms). You can set the different template for your category using administration. For details and explanation of the whole concept, have a look at Defining custom template for category.

Context will always contain at least:

Key Value
category Category object itself.
is_homepage Flag telling you if this is a homepage, see Homepages.
is_title_page Boolean telling you if this is the first page of the listing/archive.
is_paginated Boolean which is True more pages are available.
results_per_page Number of objects per page.
page Current page shown.
listings Objects listed in the category for this page.
content_type If filtering by Content Type is active, this will hold the ContentType instance.
content_type_name Verbose name of content type if Content Type filtering takes place.

The basic scenario when building up site’s category templates is following:

  1. Create the base template page/category.html. Make this template as generic as possible to enable nice inheritance. Most often, this category will be created as generic, paginated, content listing as seen on most sites using articles.
  2. Create customized template for homepage since it has different layout in most cases. Use proper fallback to tell Ella that it should use a different template for HP. It’s as simple as putting the template to page/category/[YOUR_HP_SLUG]/category.html. Also, practice inheritance, make this template using {% extend "page/category.html" %}.
  3. Create other category templates that need customization. You will most likely end up building some static pages.
Homepages

In Ella, a homepage is recognized as the category, that has no parent. Therefore, it is also the root category. Only one such page is allowed for each site that is contained in database.

The URL of homepage is always “/”, so for a domain example.com, full URL of root category would be of course the root of the whole site:

http://www.example.com/

When working with category templates, homepage will set the variable is_homepage in the template’s context to True. Thanks to it, something like this is possible:

<!-- in page/category.html -->
{% if is_homepage %}
   This is homepage category.
{% else %}
   This is not a homepage.
{% endif %}

This makes it very easy to have only one template which covers most of the category pages including homepage. However, you should always consider splitting the HP-specific code to it’s own template when the HP layout is completely different from other categories. This would make your templates much more readable which is always a good thing.

Other categories

In most Ella sites, categories other than HP usually serve for content listings or static pages. We’ll demonstrate the basic code for content listing for the sake of completness.

{% extends "page/base.html" %}

{% block content %}
    {% block object_listing %}
        {% listing 10 for category as category_listing %}
        {% for l in category_listing %}
            {% box listing for l.publishable %}{% endbox %}
        {% endfor %}
    {% endblock %}
    {% block pagination %}
       {% if is_paginated %}{% paginator 5 %}{% endif %}
    {% endblock %}
{% endblock %}
Defining custom template for category

By default, template used for rendering category is category.html. You can override this behavior to use your custom template. This can be useful when you need to implement several different layouts for your categories. Suppose we have following layouts:

  • Top 4 articles, then listing of 6 more
  • Listing of 10 articles without top ones
  • Listing of 10 articles without perexes, only big images

If it wasn’t possible to select a template for category, you would need to override the template for each category diferrent from the base one (let it be the first one). Using different templates, you can avoid doing so. First, define the templates in your settings.py:

# in settings.py
CATEGORY_TEMPLATES = (
    ('category.html', 'default (top 4 + listing 6)'),
    ('category_10.html', 'top 10'),
    ('category_10_no_perex.html', 'top 10 w/o perexes'),
)

Note

To be consistent with the Ella guidelines, please always use category.html as your base category template.

Next, create the base template. That would be category.html. It would be used, when not set otherwise in your Ella administration:

<!-- in page/category.html -->
{% extends "page/base.html" %}

{% block object_listing %}
    <!-- show 4 boxes with big images -->
    {% listing 4 for category as category_listing %}
    {% for l in category_listing %}
        {% box listing_big_image for l.publishable %}{% endbox %}
    {% endfor %}

    <!-- show 6 more regular boxes -->
    {% listing 6 from 4 for category as category_listing %}
    {% for l in category_listing %}
        {% box listing for l.publishable %}{% endbox %}
    {% endfor %}
{% endblock %}

Then, you would create category_10.html template to show only ten same boxes for listing:

<!-- in page/category_10.html -->
{% extends "page/category.html" %}

{% block object_listing %}
    <!-- show 10 same boxes -->
    {% listing 10 for category as category_listing %}
    {% for l in category_listing %}
        {% box listing for l.publishable %}{% endbox %}
    {% endfor %}
{% endblock %}

Finally, create the last category_10_no_perex.html template, that would define the last layout:

<!-- in page/category_10_no_perex.html -->
{% extends "page/category.html" %}

{% block object_listing %}
    <!-- show 10 boxes without perexes -->
    {% listing 10 for category as category_listing %}
    {% for l in category_listing %}
        {% box listing_no_perex for l.publishable %}{% endbox %}
    {% endfor %}
{% endblock %}

This way, you don’t need to override template for each of different categories, you just set the layout in your administration. Also, this is widely used when it comes to creating Static pages that don’t ever change.

Object detail page

The object detail in Ella terminology is a detail of a publishable object. This can be the article itself, a page showing gallery or a page with a video player we used as example in Plugins section. This would be a main interest for your users, the main source of information on your site.

Similarly to categories, object details use object.html template. Same fallback rules apply (see Template fallback mechanisms).

When dealing with object detail, you can be sure the context will provide you with following data:

Key Value
object Publishable subclass instance we are dealing with.
category Related Category object for this page.
content_type ContentType instance of the object.
content_type_name Verbose name of content type if Content Type.

Defining templates follows a same pattern as when working with categories:

  1. Define a generic template that will be used when rendering objects without some special behavior. In this template, try to use only attributes defined by Publishable model, so it will work for all subclasses correctly.
  2. Define custom templates for objects of different kinds. There would mostly likely be different templates for articles, galleries etc. These templates go to page/content_type/[APP_LABEL].[MODEL_NAME]/object.html, e.g. page/content_type/articles.article/object.html.
  3. Define templates for custom layout of object in specific categories. These might be sometimes required. Imagine a situation when you need an article detail to look differently in some special category. For example, you can have normal articles and site news, both of which are internally implemented as Article instances. It makes sense for site news to keep a little different layout than normal articles do, you probably won’t show the news source and so on.

To provide some real world example of basic object page, have a look at this small snippet:

<!-- in page/object.html -->
{% extends "page/base.html" %}

{% block content %}
    <!-- show photo if available -->
    {% if object.photo %}
        {% box object_main for object.photo %}{% endbox %}
    {% endif %}

    <!-- show basic information, title, authors, publication date -->
    <h1>{% block object_title %}{{ object }}{% endblock %}</h1>

    <p>Published at: <span>{{ object.publish_from|date }}</span></p>
    {% if object.authors.exists %}
        <p>Authors: <strong>{{ object.authors.all|join:", " }}</strong></p>
    {% endif %}

    <!-- render perex/description -->
    {% block perex %}
        {% render object.description %}
    {% endblock %}

    <!-- body for publishable subclasses goes here -->
    {% block body %}{% endblock %}

    <!-- show related objects -->
    {% block related %}
        {% related 5 for object as related %}
        {% for r in related %}
            {% box related for r %}{% endbox %}
        {% endfor %}
    {% endblock %}
{% endblock %}

Most likely, you would also add following things to the base object template:

  • Facebook like button, Twitter tweet button, Google +1 button
  • Sharing handlers - send by email, ...
  • Tags for the object
  • Comments
Object detail URL

The URL of Publishable object detail depends on publication type. As we already mentioned in Quickstart, there are two:

  • time-based publication is limited by publish_from - publish_to period. Outside of these time boundaries, object won’t be reachable on the website. Most websites only use publish_from so that the object won’t disappear.
  • static publication is not limited by time and thus it is unlimited and permanent. Such object will be always reachable on the website.

With time-based publications, objects are given a date stamp in the URL so the namespaces clashes doesn’t happen very often. URL structure goes like:

/category/tree/path/[YEAR]/[MONTH]/[DAY]/[CONTENT_TYPE_NAME]/slug/

So for an example, /about/2007/08/11/articles/ella-first-in-production/ could be proper result of time-based publication.

With static publication, no date stamp is used. Instead, object’s primary key is prepended before slug to avoid name conflicts. URL structure looks like this:

/category/tree/path/[CONTENT_TYPE_NAME]/[PK]-slug/

And a valid result could be /about/articles/1-ella-first-in-production/.

Archive pages

Rich-text fields: using WYSIWYG editors or a markup language

Integrating custom views

Ella doesn’t force you to make your views any prescribed way. You can easily create any Django application and add it to your project standard Django way and Ella won’t stand in way.

However, if you try to extend the functionality of the framework itself, you might want to have a look at Ella plugins which offer several simple interface for extending the Ella.

Defining positions on the page

Position is understood as placeholder on the page whose context is specific to the category in use. It allows designers to specify areas of the template to be overriden by the site writers, editors via the admin interface. Position is identified by it’s name. Main use case of positions is box embedding, but raw HTML can be used as well.

inheritance
When called from the template tag, the application will first try and locate the active position for the given category, then, if such position is not available, it will locate active position in the closest ancestor of the category. This behavior can be overriden by the nofallback argument to the {% position %} templatetag.
tied to objects or raw HTML
You can either define a generic foreign key to any object whose box you wish to display instead of the templatetag or, if the generic foreign key is empty, raw HTML that you wish to insert.
{% ifposition %} templatetag
You can check if any position for a given set of names is active using the {% ifposition %} templatetag. It behaves in same way as common {% if %} templatetag.

Note

This feature is part of ella.positions app and thus needs to be added to INSTALLED_APPS before use.

Using positions in your pages

Position is defined in the admin interface and used from the templates via two templatetags.

{% position %} template tag

Render a given position for category.

Syntax:

{% position POSITION_NAME for CATEGORY [using BOX_TYPE] [nofallback] %}
  ...
{% endposition %}

Parameters:

Name Description
POSITION_NAME Name of the position for lookup.
CATEGORY The category for which to render the position - either a Category instance or category’s slug.
BOX_TYPE Default type of the box to use, can be overriden from the admin.
nofallback If present, do not fall back to parent categories.

Text inside the tag (between {% position %} and {% endposition %}) is passed to Box used for rendering the object. This can also be overriden from the database.

{% ifposition %} template tag

Render template according to the availability of given position names within given category.

Syntax:

{% ifposition POSITION_NAME ... for CATEGORY [nofallback] %}
    POSITION EXISTS!
{% else %}
    NO POSITION DEFINED!
{% endifposition %}

Renders ‘POSITION EXISTS!’ if any of the space separated position name is active for the given category, ‘NO POSITION DEFINED!‘ otherwise.

Real world examples

Positions are widely used for a lot of page parts that need to be edited by site staff from time to time, like:

  • Site menus (see Creating site navigation)
  • Page sidebars (see Category-specific sidebars)
  • Top articles on the hompage, which are under strict supervision of editors who need to control what exactly and in which order is being displayed.
  • Carousel-like content on the bottom of the pages.
<!-- in page/category.html -->
{% load positions %}
...

{% block right_column %}
    {% position rightcol_poll for category %}{% endposition %}
{% endblock %}

...

This simple example can be used to show a poll in the page right column in case the poll is defined. It will also switch the poll for the categories where the specific one is defined as stated before.

Working with photos

Ella’s core has an integrated photo module which is tightly coupled with the rest of the modules (articles, ...) and plugins, notably the Newman administration plugin.

Features:

  • Photo format definition with cross-site option.
  • Scaling, cropping.
  • Definition of important box for automatic cropping while keeping the important area on the photo intact (e.g.: keeping faces on cropped photo).
  • {% img %} template tag for template usage.

Photo module is composed from several important parts:

Photo model
Photo model stands for the actual photo uploaded by user.
Format model
Describes different formats that a sites is using. Think of format as a set of rules how to render: “a big photo aligned to right side”, “small photo to show authors face” and so on.
FormatedPhoto model
This model keeps track of photos that have already been formatted in a given format. It works like a cache so that the formatting only occurs once.
{% img %} template tag
{% img %} is used when placing the photos in the templates. It simplifies and abstracts the process of thumbnail creation.
Generating thumbnails in the tempalates

The {% img %} template tag is used to get a thumbnail for original Photo object. It is smart enough to use all the meta info defined on Photo, so the important box is taken into account.

Syntax:

{% img <FORMAT> for <VAR> as <VAR_NAME> %}
{% img <FORMAT> with <FIELD_VALUE> as <VAR_NAME> %}

Templatetag supports two approaches. First is very simple, you just give it a Photo instance and it will generate thumbnail for it. The second one tries to find a Photo you describe by FIELD_VALUE. See the examples:

{% img category_listing for object.photo as thumb %}
{% img category_listing with pk 1150 as thumb %}

The result (stored as thumb in the example above) then contains a FormatedPhoto instance. This means you can access it’s attributes, particularly url method and width and height.

Workflow

The basic workflow when using photos goes like this:

  1. Define formats. This step is usually already done when you enter the stage as the designer is reponsible for it in most cases. We only need to enter the data to the Ella database.

  2. Store the formats in fixtures is quite important step, because it makes development much easier when a more than one developer is involved. It makes sense to add the fixture as initial data because it shouldn’t be altered in database without an intent.

  3. Use image boxes in your templates. For the thumbnails, use boxes. The snippet below shows how you can embed photos using boxes in an object box we used in Category detail page section.

    <!-- in box/listing.html -->
    <div class="article">
        <h2><a href="{{ object.get_absolute_url }}">{{ object }}</a></h2>
        {% if object.photo %}
            <a href="{{ object.get_absolute_url }}" title="{{ object.title }}">
                {% box category_listing for object.photo %}{% endbox %}
            </a>
        {% endif %}
    </div>
    
  4. Use image templatetag to generate thumbnails. When the photo is embedded, the last remaining step is to generate thumbnails so the photo will fit on the page nicely. To do this, use {% image %} template tag.

    <!-- in box/content_type/photos.photo/category_listing.html -->
    {% load photos %}
    
    {% block image_tag %}
        {% image object in "200x100" as image %}
        <img src="{{ image.url }}" alt="{% firstof title object.title %}" width="{{ image.width }}" height="{{ image.height }}" />
    {% endblock %}
    

Note

It’s a good habit to use format naming convention which describes the used dimensions (like the “200x100” used in example above) and attributes because:

  • It will minimize the number of formats you use and eliminate duplicates.
  • It will eliminate the threat that the same image is formatted twice with same parameters.
Using placeholder images during development

It is quite common that during development of the Ella application, one doesn’t always have all the photos stated in database on his HDD. This can happen when you share one database dump with co-workers and someone adds new articles etc.

In order to show at least something, Ella provides debugging setting which will replace the missing image files by placeholder images. You can enable this by setting PHOTOS_DEBUG = True in your project settings. By default, Ella will use web service http://placehold.it to generate the images. Optionally, you can use your custom placeholder service by changing the PHOTOS_DEBUG_PLACEHOLDER_PROVIDER_TEMPLATE to your own. Use something like this:

DEBUG_PLACEHOLDER_PROVIDER_TEMPLATE = 'http://placehold.it/%(width)sx%(height)s'

Syndication - ATOM and his RSS friend

Ella has automatic syndication support out of the box. For each category, there are RSS a ATOM feeds automatically available on:

www.example.com/feeds/rss/[CATEGORY_TREE_PATH]/

and:

www.example.com/feeds/atom/[CATEGORY_TREE_PATH]/

respectively.

Ella uses Django syndication feed framework internally to render the feeds. Default number of objects in feed is set to 10 by RSS_NUM_IN_FEED setting. You can override this setting it to different value in your settings.py. Also, you can define an enclosure format by setting RSS_ENCLOSURE_PHOTO_FORMAT which defaults to None. The value is expected as Format instance name. If you set this to None (or don’t set it at all), no enclosures will be used.

The feed title and description defaults to category title attribute. If you need to override this, use app_data and make sure you set following:

category.app_data['syndication'] = {
    'title': 'My feed title',
    'description': 'My feed description'
}

You can do this through Django administration.

Incorporating plugins

Ella design is as lightweight as possible. Prefered way of extending it’s functions is via plugins. Ella provides great flexibility when it comes to plugin possibilities. You can for example:

  • Add your custom Publishable subclasses.
  • Create custom Box classes for the new publishables.
  • Add new actions over the Publishable objects.
  • Customize bundled workflow when rendering the content.

We’ve dedicated whole section for plugins, because it’s an important topic and almost every project has it’s specific needs. So, for details, go to Plugins.

Extending category/publishable metadata

Since Ella has quite a long history behind it, we’ve gathered lot of experience from previous fails. One such experience is that almost every project needs to add aditional data on the bundled models. This can be done in lot of various ways because of Python’s great possibilities, but more or less, it’s a dark magic or monkey patching. This is not nice and violates the Django core principle: explicit is better then implicit. To fix this up, we’ve added possibility to add arbitrary data on Publishable, Category and Photo models programatically.

Each of the mentioned models has one JSONField subclass AppDataField called app_data which can hold any information you need. It has some limitations though:

  • It’s not possible to perform efficent queries over the defined fiels. If you needed it, add OneToOne relation to your custom model instead.
  • You are responsible of setting the fields correctly, no validation measures are placed on that field so that the data might be corrupted if not used properly.

AppDataField acts the same way as regular Python dict object. You can store any data structure you like provided it’s serializable by Django’s JSON encoder.

Conventions

AppDataField recognizes namespaces for different applications. The access is not limited though so that any application can access any namespace. The namespace is a simple first-level dict key (e.g. ‘emailing’ or ‘my_articles’ in the following examples).

To avoid name clashes, we encourage you to follow this convention so that all your custom data is stored by using a key which coresponds to the app label of aplication storing the data, such as:

# in app "emailing"
p = Publishable()
p.app_data['emailing'] = {'sent': False}

# in app "my_articles"
p = Publishable()
p.app_data['my_articles'] = {'custom_title': 'Foobar'}
Custom container classes

By default, Ella returns an AppDataContainer class when you access a namespace. This is simply a dict subclass with no additional data except for the information about the model class it is bound to. However, you can provide your own classess for the namespaces. This allows you to create methods working with the your custom data. For example, you can have CommentsDataContainer for your comments application which can provide methods like comment_count.

Registering your custom container class is very simple. The formula is:

from app_data import app_registry
app_registry.register('comments', CommentsDataContainer, Publishable)

Unregistration works the same way:

app_registry.unregister('comments', Publishable)
app_registry.register('comments', SomeOtherDataContainer, Publishable)

Caching

Double rendering

Deployment

Plugins

Ella tries to keep the core framework as lightweight as possible. This has very good reasons:

  • it let’s what is required for your app to your decision
  • it minimizes the dependencies of core application
  • it makes the development faster, because you can enhance your module while not needing to update the rest of the code

Because of this philosophy, plugins were introduced in version 3.0.0.

Where to get ‘em?

Currently, there are only plugins that were created by us directly. 3rd party plugins are hopefully on the way – it depends more o less on you, Mr. Reader :)

Our plugins can be found in our GitHub repository. Here is a list of some interesting ones:

  • Newman - use admin forged directly for Ella needs
  • Tagging - provides tagging functionality
  • Comments - simple threaded comments
  • Galleries - create galleries from your photos
  • Polls - let users vote for things and compete
  • Imports - load stuff from other sites
  • Series - create series from articles covering same topic

Basic plugin structure

All Ella plugins come as Django applications bundled using setuptools. Each plugin has dependency on the Ella’s core, so Ella is always required and plugins can hardly be used without it.

As Ella provides significant flexibility, plugins are able to do quite a lot of magic, like following:

  • Define custom Publishable objects via subclassing. For more details, see Subclassing Publishable.
  • Extend actions performed over the Publishable objects, for details, see Overriding Publishable URLs section.
  • Create custom Box classess for fine-tuned includes. This is discussed in detail in section Custom Boxes.
  • Provide additional methods of listing Publishable objects for a category, see Listing Handlers.
  • Define related finder functions to get related Publishable instances, see Related finders.

Plugin API

Subclassing Publishable

Due to the fact that Publishable is common Django model, it is possible to simply extend it with your custom model. When doing this, you effectively adding your custom model to whole Ella machinery and all publishing-related stuff is ready for you out-of-the box!

Let’s have a look at real-world example. During this walkthrough, we will try to create a publishable video that will have YouTube video code as source. We will keep it simple and use YouTube’s video player to show the video itself.

The very first step is to create your own application using the standard Django way:

django-admin.py startapp video

After creating the app to hold it, let’s define the class itself. If you are acustomed to Django models, this is going to look very familiar to you.:

# in models.py within your video application

from django.db import models
from django.utils.translation import ugettext_lazy as _

from ella.core.models import Publishable

class YoutubeVideo(Publishable):
    code = models.CharField(max_length=20, title=_('Code'))

    class Meta:
        verbose_name = _('Youtube video')
        verbose_name_plural = _('Youtube videos')

    def __unicode__(self):
        return self.code

Note

If you want to examine whats is going on behind the scenes when subclassing Django models, have a look at Django model inheritance. We are using Multi-table inheritance so that each Publishable subclass has a hidden pointer - publishable_ptr - to the Publishable object. This pointer also acts as PK in the DB table.

Next step is to put the new app to the settings.:

INSTALLED_APPS = (
    'ella.core',
    'ella.articles',
    ...
    'yourproject.video',
)

Finally, resync your database so the DB table for the class is created:

django-admin.py syncdb

By defining this, we already have a working publishable object, nothing more is needed. Of course, in real world, you would probably need to do some polishing (adding better title, etc.), but for now, this is enough.

Custom Boxes

We have defined our new publishable object, but something still remains a little unclear: how to embed the video in the HTML page. In this part of walkthrough, we will present you a way that is preferred when working with Ella. These are so-called boxes.

As described in Boxes, boxes are something you can call an include on steroids. Boxes behave very much like standard Django {% include %} templatetag, but are suited to be used with publishable objects. They do following things for you so you don’t need to care about them:

  • Template path resolution
  • Object-specific context within the included template
  • Ability to accept advanced parameters

Now back to the Video publishable subclass. What we want to achieve is that our Video is being rendered in the page. For this, we will create a custom Box subclass. Here is, how a desired result will look when embedding the video in the page:

<h1>Watch the video right here!</h1>

{% box video_player for video_object %}
    width: 400
    height: 200
{% endbox %}

The first thing you need to do is to define the box sublcass itself:

# in models.py
from ella.core.box import Box

class VideoBox(Box):
    def get_context(self):
        context = super(VideoBox, self).get_context()
        context.update({
            'width': self.params.get('width', '400'),
            'height': self.params.get('height', '200')
        })
        return context

Note the get_context method. Since width and height parameters are specific to our VideoBox and not recognized by other boxes, we need to handle them and pass them into the include context. self.params is a dictionary holding parameters used to call the box. We provide sane defaults when the parameters are not provided so that we can still call the box by using simple {% box video_player for video_object %}{% endbox %}.

Next step is to let Ella know, that we want a special type of box to be used with our Video. If we didn’t do that, Ella would use a basic Box class which is missing the width and height parameters. To tie our model with the VideoBox set the box_class class variable on the Video model:

class Video(Publishable):
    ...
    box_class = VideoBox
    ...

In order to actually render something we also need to create a HTML template. Box templates are placed in box directory within paths where Django template finders are able to reach them (if you are unsure what a template finder is, please refer to the Docs). The name of the box also serves as name of the template to use. In our case, the name of the box is video_player so the template name is going to be video_player.html. Boxes provide a template search fallback which we’re not gonna discuss here to keep the thing simple. For further information, see Templatetags.

Our box is fairly simple. We are gonna use the code provided by YouTube and it will look like this:

<!-- in templates/box/video_player.html -->
<iframe width="{{ width }}" height="{{ height }}" src="http://www.youtube.com/embed/{{ object.code }}" frameborder="0" allowfullscreen></iframe>

See how nicely box integrates all things we have so far together. It uses object.code to build up the URL and width and height attributes to define the video player dimensions.

Overriding Publishable URLs
Adding custom actions

Consider a situation, when we would like to have discussion about the video on a separate page while keeping the nice URL prefix Ella creates for it’s publishable objects. There is a simple solution for that. Ella’s custom URL resolver allows us to add actions for the Publishable objects easily.

We would like our URL to have following form:

/about/2007/08/11/videos/ella-first-in-production/discussion/

To do this, we will append a custom view function for the Video model:

# in yourapp/video/urls.py
from django.conf.urls.defaults import url, patterns

from ella.core.custom_urls import resolver

from yourapp.models import Video
from yourapp.video.views import show_discussion

urlpatterns = patterns('',
    url(r'^discussion/$', show_discussion, name='video-show-discussions'),
)

resolver.register(urlpatterns, model=Video)

When registering custom URLs, we use ella.core.custom_urls.resolver instead of regular Django url machinery. This does a little Python magic so that your URLs will be appended to base Publishable URL. Note the use of model argument in

resolver.register(urlpatterns, model=Video)

This means, that the custom action will be available only for a Video model. If we wanted to add our discussion action to all Publishable models, we would simply omit the model argument altogether.

As you have probably noticed, we are using show_discussion view function without declaring it, let’s fix that up:

# in yourapp/video/views.py

def show_discussion(request, context):
    obj = context['object']
    return render('yourapp/discussion.html', {'object': obj})

Views that are used with Ella’s resolver always accept request (which is a normal Django request object) and context which is a dictionary that contains following data:

Custom view arguments
Key Value
object The publishable object itself.
category Category object related to the URL.
content_type_name Verbose name of Content type of the Publishable (e.g. Article, Video and so on).
content_type ContentType instance for Publishable.
Overriding objects’ detail

Besides custom actions, it is also possible to completely override the view which handles rendering of object detail page. Such a requirement might occur in these situations:

  • You need to add custom object-related data to the context in the detail template.
  • You wish to change the way the view itself works. This can be used for advanced behavior changes which are needed only rarely.

To define a custom view, we will use the Ella’s URL resolver again:

# in your urls.py
from django.conf.urls.defaults import patterns, url

from ella.core.custom_urls import resolver

from yourapp.models import Video
from yourapp.views import video_detail

resolver.register_custom_detail(Video, article_detail)

This will result in calling our article_detail view instead of the default one. Custom detail views are called with same arguments as custom action views. For reference, see the table with custom view arguments above. For further information on Ella’s ObjectDetailView, see Views.

Listing Handlers

By default Ella can list Publishable in categories using the Listing model. If additional methods of listing, sorting and pagination is required, a plugin can define a ListingHandler. For example if makes sense for a comments plugin to define a ListingHandler that will allow you to list Publishables sorted by number of comments.

Project can define multiple ListingHandler classes and use GET parameters and optional arguments to the {% listing %} template tag to determine which to use on per-request basis.

On top of the default ListingHandler ('ella.core.managers.ModelListingHandler') Ella also provides an optimized RedisListingHandler ('ella.core.cache.redis.RedisListingHandler') to be used on high traffic sites.

Usage

ListingHandler classes a projects is using are defined in settings, a configuration entry for 'default' must always be present:

LISTING_HANDLERS = {
    'default': 'ella.core.managers.ModelListingHandler',
    'comments': 'ella_comments.CommentCountListingHandler',
}
Interface

ListingHandler is justa class that defines two methods - count and get_listings:

from ella.core.managers import ListingHandler

class CommentsListingHandler(ListingHandler):
    def count(self):
        ...

    def get_listings(self, offset=0, count=10):
        ...

The __init__ method of the class accepts folowing aguments: category, children=None, content_types=[], date_range=(), exclude=None

ListingHandler.__init__ arguments
Key Default Value
category   Category object
children NONE One of NONE, IMMEDIATE and ALL indicating whether to include all Publishables listed in category’s children/descendants.
content_types [] ContentType instances to filter on.
date_range () Optional date range to list.
exclude None A Publishable instance to omit from the result.

API reference

If you are looking for the description of the models, views and more, you’re on the right place.

Models

Ella core module consist of four apps, two of them contain the main logic and the second two provide basic CMS capabilities. The core logic is provided by ella.core and ella.photos applications, see the image below for quick overview of models used. Moreover, Ella ships with two basic CMS apps you can use: articles and positions. Quick overview image follows.

_images/core_models.png _images/photos_models.png _images/cms_models.png
Core models
The Author model
class ella.core.models.Author

Describes an Author of the published content. Author can be:

  • Human
  • Organization
  • ...

All the fields except for slug are optional to enable maximum of flexibility.

Author.user

Optional. Related Django User instance. Can be blank and null.

Author.name

Optional. Name of the author.

Author.slug

Required. The only required field is slug. Must be unique.

Author.description

Optional. You may provide description of author.

Author.text

Optional. Small perex about the author. Use together with description to provide information about the author.

Author.email

Optional. When dealing with humans as authors, you can fill up his e-mail.

The Source model
class ella.core.models.Source

A Source in oposition to Author is used for publishable content that was taken from other sites and it’s purpose is mainly for legal matters.

Source.name

Required. The name of the source.

Source.url

Optional. If source is an organization, you may fill up their URL.

Source.description

Optional. Description about the source.

The Category model
class ella.core.models.Category

Category is the basic building block of Ella-based sites. All the published content is divided into categories - every Publishable object has a ForeignKey to it’s primary Category. Primary category is then used to build up object’s URL when using Category.get_absolute_url method. Besides that, objects can be published in other categories (aka “secondary” categories) via Listing.

Every site has exactly one root category (without a parent) that serve’s as the sites’s homepage.

Attributes
Category.title

Title of the category for common purposes.

Category.description

Optional. Description of the category for common purposes.

Category.content

When rendering static pages, this can come handy. It’s a rich-text powered field capable of holding HTML.

Category.template

Required. Template used for rendering the category. Defaults to category.html and can be overriden for custom layouts of the category detail.

Category.slug

Required. Slug for querying the category in URL.

Category.tree_parent

Relation to the parent category. When no parent category exists, the value is None and such category is considered as root category. The prefered way of getting the parent is Category.get_tree_parent method insted.

Category.main_parent

Returns parent category, which is considered as main. A main category is a category, whose parent is the root category.

Category.tree_path

Path in the category tree from the root. Is composed from the slug fields of the categories on the way joined by “/” symbol. It’s generated automatically.

Example: "nested1/nested2"

Category.path

Returns tree path of the category. Tree path is string that describes the whole path from the category root to the position of this category.

Category.site

Required. A Site from django.contrib.sites framework, which category belongs to.

Category.app_data

Optional. A JSONField for keeping arbitrary data. See Extending category/publishable metadata for further information.

Methods
Category.get_tree_parent(self)

Returns tree parent or None if not existent. Cached. Use in favor of Category.tree_parent attribute.

Category.get_absolute_url(self)

Returns absolute URL of the category. Useful in templates and views.

Category.draw_title(self)

Returns title indented by &nbsp; elements that can be used to show users a category tree.

Examples:

no direct parent (the category root)
TITLE
one parent
&nsbp;TITLE
on third level of the tree
&nbsp;&nbsp;TITLE
The Dependency model
class ella.core.models.Dependency
Dependency.target_ct

ContentType of the Dependency.target.

Dependency.target_id

Primary key of the Dependency.target.

Dependency.target

Target of the dependency relation.

Dependency.dependent_ct

ContentType of the Dependency.dependent

Dependency.dependent_id

Primary key of the Dependency.target.

Dependency.dependent

Source of the dependency relation.

The Publishable model
class ella.core.models.Publishable

Base class for all objects that can be published in Ella.

Attributes
Publishable.content_type

Automatically managed. ContentType instance of the Publishable subclass if applicable. Used to get the subclass instance in case a generic Publishable parent is dealt with which occurs for example when querying over all publishable objects.

Publishable.target

Automatically managed. Generic foreign key that points to the subclass instance for easy access. Cached to save the queries.

Publishable.category

Required. Main Category object a Publishable instance belongs to. This has significant impact when building up the URL.

Publishable.title

Required. Verbose title of the publishable (gallery name, article title, ...).

Publishable.description

Optional. Basic description of the publishable. Can be used as perex, for instance.

Publishable.slug

Required. Slug to use when building up the URL. Needs to URL-friendly.

Publishable.authors

Required. A ManyToMany relation with Author model to list publishable object’s authors.

Publishable.source

Optional. If the object comes from diferent source which needs to be listed, use this field.

Publishable.photo

Optional. The main photo of publishable objects. Main article photo for example.

Publishable.published

Required. A BooleanField instance keeping information if the object is already published or not. Works together with publish_from and publish_to fields.

Publishable.publish_from

Required. Datetime keeping the start of publication period.

Publishable.publish_to

Required. Datetiem keeping the Finish of publication period.

Publishable.static

Required. A boolean whether the publication is static which means it’s not bound to some special date. Publication is valid forever.

Publishable.app_data

Optional. A container for arbitrary data on the model, for more info, see Extending category/publishable metadata.

Methods
Publishable.get_absolute_url(self, domain=False)

Returns absolute URL to the object without the domain and protocol.

Example: "/news/2012/1/1/some-article-slug/"

Publishable.get_domain_url(self)

Returns full URL to the object with the domain and protocol added.

Example: "http://www.example.com/news/2012/1/1/some-article-slug/"

Publishable.get_domain_url_admin_tag(self)

Domain url to be used in adminstration for showing the page link.

Publishable.is_published(self)

Returns True if the Publishable is currently active, False otherwise.

The Listing model
class ella.core.models.Listing

Listing of an Publishable in a Category. Each and every object that have it’s own detail page must have a Listing object that is valid (not expired) and places it in the object’s main category. Any object can be listed in any number of categories (but only once per category). Even if the object is listed in other categories besides its main category, its detail page’s url still belongs to the main one.

Attributes
Listing.publishable

Required. A related Publishable instance to define the listing for.

Listing.category

Required. A Category instance where the listing should occur.

Listing.publish_from

Required. Datetime with start of the listing period.

Listing.publish_to

Required. Detaime with end of the listing period.

Listing.commercial

Optional. Set to True if the listing is a commercial or ad related. These listings are usually marked with an ad warning. Defaults to False.

Methods
Listing.get_absolute_url(self, domain=False)

Returns absolute URL to the listing without domain and protocol parts.

Example: "/news/2012/1/1/some-article-slug/"

Listing.get_domain_url(self)

Returns absolute URL to the list with the domain and protocol.

Example: "http://www.example.com/news/2012/1/1/some-article-slug/"

Photo models
The Photo model
class ella.photos.models.Photo

Represents original (unformated) photo uploaded by user. Used as source object for all the formatting stuff and to keep the metadata common to all related FormatedPhoto objects.

Attributes
Photo.title

Required. Human-readable title of the photo.

Photo.description

Optional description.

Photo.slug

Required. Slug to use when creating URL.

Photo.image

Required. Path to the uploaded image file.

Photo.width

Required. Original width of the uploaded image file.

Photo.height

Required. Original height of the uploaded image file.

important_* attributes describe the rectangular area on the photo, which shouldn’t be cropped.

Photo.important_top
Photo.important_left
Photo.important_bottom
Photo.important_right
Photo.authors

Required. A ManyToMany relation with Author model.

Photo.source
Photo.created

Automatically managed. Keeps information when the photo was uploaded.

Photo.app_data

Optional. A container for arbitrary data on the model, for more info, see Extending category/publishable metadata.

Methods
Photo.__unicode__()

A human-readable representation of the Photo.

Photo.get_absolute_url()

Full URL to the image file.

Photo.get_image_info()

Returns dictionary with keys url, width and height holding metainformation about the image.

Example:

>>> p = Photo.objects.get(pk=1)
>>> p.get_image_info()
>>> {'url': 'http://media.example.com/2011/1/23/img.jpg', 'width': 100, 'height': 200}
Photo.ratio()

Returns float holding the ratio between width and height of None if not applicable.

Photo.get_formated_photo(self, format)

Returns FormatedPhoto instance for given format.

The Format model
class ella.photos.models.Format

Defines per-site photo sizes together with rules how to adhere to them.

This includes:

  • maximum width and height
  • cropping settings
  • stretch (rescale) settings
  • sample quality
Attributes
Format.name
Format.max_width

Required. Integer with maximum width in pixels of the resulting image.

Format.max_height

Required. Integer with maximum height in pixels of the resulting image.

Format.flexible_height

Required. Boolean if height is “flexible”. If set to True, the allowed height will be in range max_height - flexible_max_height.

Format.flexible_max_height

See Format.flexible_height above.

Format.stretch

Required. True if stretching can be used to ensure required dimensions. If set to False, only cropping will be used.

Format.nocrop

Required. True if this format doesn’t do any cropping.

Format.resample_quality

Requried. Sampling quality to use when performing formating operations. Defaults to 85.

Format.sites

Django Site instances that can use the format.

Methods
Format.get_blank_img(self)

Returns fake FormatedPhoto object to be used in templates when an error occurs in image generation. The result will be a dictionary with keys blank, width, height and url which points to storage while using PHOTOS_EMPTY_IMAGE_SITE_PREFIX setting.

Format.ratio(self)

Returns float holding the ratio between width and height.

The FormatedPhoto model
class ella.photos.models.FormatedPhoto

Cache-like container of specific photo of specific format. Besides the path to the generated image file, crop used is also stored together with new width and height attributes.

Attributes
FormatedPhoto.photo

Related Photo instance that is being formated.

FormatedPhoto.format

Related Format instance that is being used for formating.

FormatedPhoto.image

Source Image instance.

The crop_* attributes keep information how the cropping was done if peformed.

FormatedPhoto.crop_left
FormatedPhoto.crop_top
FormatedPhoto.crop_width
FormatedPhoto.crop_height
FormatedPhoto.url

Returns the URL of the resulting photo file.

Methods
FormatedPhoto.generate(self, save=True)

Generates photo file in current format.

If save is True, file is saved too.

FormatedPhoto.remove_file(self)

Deletes the formated file.

FormatedPhoto.file(self)

Returns instance of the formated file.

The Article model
class ella.articles.models.Article

Article is the most common publishable object. It can be used for news on internet news pages, blog posts on smaller blogs or even for news on an organization’s home page.

Attributes
Article.upper_title

Optional. Second title to use for special use cases.

Article.created

Automatically managed. Datetime when the article was created.

Article.updated

Set by user, optional. Datetime when the article was updated. This is not updated automatically and is in the control of users. Can be used for information to the readers when the article was last updated.

Article.content

Required. Rich-text field holding content of the article.

Methods
Article.article_age(self)

Returns time since article was created in localized, verbose form.

Examples: “three days ago”, “few minutes ago”

The Position model
class ella.positions.models.Position

Represents a position – a placeholder – on a page belonging to a certain category.

Attributes
Position.name

A human-readable name for the position. This name is also used in templates when using the {% position %} and {% ifposition %} templatetags.

Position.category

A Category object for which the position si defined. This is very important and used when resolving which Position object to use for the place defined in template.

Position.target_ct

Optional. Django ContentType instance for the object to show in the position. Used together with target_id to find out the final target.

Position.target_id
Position.target

Optional. Instance of the target object. In case nor target_ct nor target_id is set, raw HTML is rendered using the text field instead.

Position.text

Optional. When no specific object is bound to the position using the target attribute, raw HTML in this field is used.

Position.box_type

Optional. Box name to use when rendering taget.

Position.active_from

Optional. Datetime holding information when to start showing this position. If kept to None, no check is performed.

Position.active_till

Optional. Datetime holding information when to finish showing this position. If kept to None, no check is performed.

Position.disabled

Optional. Defaults to False. If set to True, position won’t be shown even though it is active.

Methods
Position.__unicode__(self)

Human-readable representation of the position.

Position.render(self, context, nodelist, box_type)

Returns the rendered position object. When position is bound to an object, box for the object will be rendered using box_type. If no object is specified, raw HTML in text attribute use used as template.

Views

Note

In any template name <tree_path> stands for the value of Category.path, not it’s actual tree_path. This is because of empty tree_path for root categories which would make it impossible to override a template for anything in the root category and the root category itself.

class ella.core.views.ListContentType

List objects’ listings according to the parameters. If no filtering is applied (including pagination), the category’s title page is rendered. The template used depends on template attribute for category being rendered. Default template is category.html, so it would look like this:

  • page/category/<tree_path>/category.html
  • page/category.html

If custom template is selected, let’s say static_page.html, it would result in:

  • page/category/<tree_path>/static_page.html
  • page/static_page.html

If filtering is active, an archive template gets rendered:

  • page/category/<tree_path>/content_type/<app>.<model>/listing.html
  • page/category/<tree_path>/listing.html
  • page/content_type/<app>.<model>/listing.html
  • page/listing.html

The context contains:

  • category
  • listings: list of Listing objects ordered by date
  • page: django.core.paginator.Page instance
  • is_paginated: True if there are more pages
  • results_per_page: number of objects on one page
  • content_type: ContentType of the objects, if filtered on content type
  • content_type_name: name of the objects’ type, if filtered on content type
Parameters:
  • categorytree_path of the Category, root category is used if empty
  • month, day (year,) – date matching the publish_from field of the Listing object.
  • content_type – slugified verbose_name_plural of the target model, if omitted all content_types are listed
  • page_no – which page to display

All parameters are optional, filtering is done on those supplied

Raises Http404:if the specified category or content_type does not exist or if the given date is malformed.
class ella.core.views.ObjectDetail

Renders a page for publishable. If url_remainder is specified, tries to locate custom view via DetailDispatcher.call_view(). If DetailDispatcher.has_custom_detail() returns True, calls DetailDispatcher.call_custom_detail(). Otherwise renders a template with context containing:

  • object: Publishable instance representing the URL accessed
  • category: Category of the object
  • content_type_name: slugified plural verbose name of the publishable’s content type
  • content_type: ContentType of the publishable

The template is chosen based on the object in question (the first one that matches is used):

  • page/category/<tree_path>/content_type/<app>.<model>/<slug>/object.html
  • page/category/<tree_path>/content_type/<app>.<model>/object.html
  • page/category/<tree_path>/object.html
  • page/content_type/<app>.<model>/object.html
  • page/object.html
Parameters:
  • requestHttpRequest from Django
  • categoryCategory.tree_path (empty if home category)
  • month day (year) – date matching the publish_from field of the Publishable object
  • slug – slug of the Publishable
  • url_remainder – url after the object’s url, used to locate custom views in custom_urls.resolver
Raises Http404:

if the URL is not valid and/or doesn’t correspond to any valid Publishable

Templatetags

Core templatetags

Core templatetags are automatically loaded for your disposal.

ella.core.templatetags.core.listing(parser, token)

Tag that will obtain listing of top objects for a given category and store them in context under given name.

Usage:

{% listing <limit>[ from <offset>][of <app.model>[, <app.model>[, ...]]][ for <category> ] [with children|descendents] [using listing_handler] as <result> %}
Parameters:
Option Description
limit Number of objects to retrieve.
offset Starting with number (1-based), starts from first if no offset specified.
app.model, ... List of allowed models, all if omitted.
category Category of the listing, all categories if not specified. Can be either string (tree path), or variable containing a Category object.
children Include items from direct subcategories.
descendents Include items from all descend subcategories.
exclude Variable including a Publishable to omit.
using Name of Listing Handler ro use
result Store the resulting list in context under given name.

Examples:

{% listing 10 of articles.article for "home_page" as obj_list %}
{% listing 10 of articles.article for category as obj_list %}
{% listing 10 of articles.article for category with children as obj_list %}
{% listing 10 of articles.article for category with descendents as obj_list %}
{% listing 10 from 10 of articles.article as obj_list %}
{% listing 10 of articles.article, photos.photo as obj_list %}
ella.core.templatetags.core.do_box(parser, token)

Tag Node representing our idea of a reusable box. It can handle multiple parameters in its body which will then be accessible via {{ box.params }} in the template being rendered.

Note

The inside of the box will be rendered only when redering the box in current context and the object template variable will be present and set to the target of the box.

Author of any Model can specify it’s own box_class which enables custom handling of some content types (boxes for polls for example need some extra information to render properly).

Boxes, same as core-views, look for most specific template for a given object an only fall back to more generic template if the more specific one doesn’t exist. The list of templates it looks for:

  • box/category/<tree_path>/content_type/<app>.<model>/<slug>/<box_name>.html
  • box/category/<tree_path>/content_type/<app>.<model>/<box_name>.html
  • box/category/<tree_path>/content_type/<app>.<model>/box.html
  • box/content_type/<app>.<model>/<slug>/<box_name>.html
  • box/content_type/<app>.<model>/<box_name>.html
  • box/content_type/<app>.<model>/box.html
  • box/<box_name>.html
  • box/box.html

Note

Since boxes work for all models (and not just Publishable subclasses), some template names don’t exist for some model classes, for example Photo model doesn’t have a link to Category so that cannot be used.

Boxes are always rendered in current context with added variables:

  • object - object being represented
  • box - instance of ella.core.box.Box

Usage:

{% box <boxtype> for <app.model> with <field> <value> %}
    param_name: value
    param_name_2: {{ some_var }}
{% endbox %}

{% box <boxtype> for <var_name> %}
    ...
{% endbox %}

Parameters:

Option Description
boxtype Name of the box to use
app.model Model class to use
field Field on which to do DB lookup
value Value for DB lookup
var_name Template variable to get the instance from

Examples:

{% box home_listing for articles.article with slug "some-slug" %}{% endbox %}

{% box home_listing for articles.article with pk object_id %}
    template_name : {{object.get_box_template}}
{% endbox %}

{% box home_listing for article %}{% endbox %}
ella.core.templatetags.core.do_render(parser, token)

Renders a rich-text field using defined markup.

Example:

{% render some_var %}
ella.core.templatetags.core.ipblur(*args, **kwargs)

blurs IP address

ella.core.templatetags.core.emailblur(*args, **kwargs)

Obfuscates e-mail addresses - only @ and dot

Custom URLs templatetags

Lot of what you see here has been stolen from Django’s {% url %} tag.

ella.core.templatetags.custom_urls_tags.custom_url(parser, token)

Get URL using Ella custom URL resolver and return it or save it in context variable.

Syntax:

{% custom_url <FOR_VARIABLE> <VIEWNAME>[[[ <ARGS>] <KWARGS>] as <VAR>] %}

Examples:

{% custom_url object send_by_email %}
{% custom_url object send_by_email 1 %}
{% custom_url object send_by_email pk=1 %}
{% custom_url object send_by_email pk=1 as saved_url %}
Related

Get N related models into a context variable optionally specifying a named related finder.

Usage:

{% related <limit>[ query_type] [app.model, ...] for <object> as <result> %}
Parameters::
Option Description
limit Number of objects to retrieve.
query_type Named finder to resolve the related objects, falls back to settings.DEFAULT_RELATED_FINDER when not specified.
app.model, ... List of allowed models, all if omitted.
object Object to get the related for.
result Store the resulting list in context under given name.

Examples:

{% related 10 for object as related_list %}
{% related 10 directly articles.article, galleries.gallery for object as related_list %}
Photos
ella.photos.templatetags.photos.img(parser, token)

Deprecated, use {% image %} instead. Generates thumbnails for Photo instances.

syntax:

{% img <format> for <var> as <var_name> %}
{% img <format> with <field_value> as <var_name> %} 

examples:

{% img category_listing for object.photo as thumb %}
{% img category_listing with pk 1150 as thumb %}
Positions
ella.positions.templatetags.positions.position(parser, token)

Render a given position for category. If some position is not defined for first category, position from its parent category is used unless nofallback is specified.

Syntax:

{% position POSITION_NAME for CATEGORY [nofallback] %}{% endposition %}
{% position POSITION_NAME for CATEGORY using BOX_TYPE [nofallback] %}{% endposition %}

Example usage:

{% position top_left for category %}{% endposition %}
ella.positions.templatetags.positions.ifposition(parser, token)

Syntax:

{% ifposition POSITION_NAME ... for CATEGORY [nofallback] %}
{% else %}
{% endifposition %}

Template overview

This doc part is ment as quick reference for templates that Ella uses. The details on the template usage are kept in the section related to the each of the templates presented.

Object detail templates

This table shows the fallback used when selecting the right template for an object. Note how the most specific template is searched for first. When no such template is found, Ella tries the find second-most specific and so on.

As a criterion for selection, two things are considered:

  • Object’s ContentType, specifically CONTENT_TYPE_NAME which is defined as app_label.model_label.
  • path attribute of the Category which object belongs to.
Path Description
page/category/[PATH]/content_type/[CONTENT_TYPE_NAME]/object.html First template to try (category and content-specific) when rendering a Publishable detail.
page/content_type/[CONTENT_TYPE_NAME]/object.html Content-type specific template to try when rendering a Publishable detail.
page/category/[PATH]/object.html Category-specific template to try when rendering a Publishable detail.
page/object.html Fallback for rendering a Publishable detail page such as article content, see Object detail page.
Category detail templates

Following table shows the template path prototypes used when selecting the suitable template for a category detail. Ella tries to find a correct template by inspecting the path attribute of the given Category object.

Note

The category.html can be overriden for specific categories via administration. This will affect the mentioned fallback so that instead of category.html Ella will search for [OVERRIDEN_NAME].html where [OVERRIDEN_NAME] stands for name of the selected template.

For the details, see Category detail page.

Path Description
page/category/[PATH]/category.html Specific template for a category detail page.
page/category.html Fallback template for rendering a category detail page, category listings, static pages.
Box templates

Box templates are distinguised only by CONTENT_TYPE_NAME as described above. The root for searching is in the box subdirectory of your templates. Also, boxes are named so that the name of the template searched is directly related to the name of the box being rendered.

Path Description
box/content_type/[CONTENT_TYPE_NAME]/[BOX_NAME].html Content-type specific template for a box named [BOX_NAME].
box/[BOX_NAME].html Named template for a box without any specific content type. These are boxes that are same for most of publishables.
box/box.html Fallback template for box templatetag when no specific template for box is found.
Listing templates

Templates for listings (see Archive pages) follow same rules as categories with the only difference is that a listing.html is used as template name.

Other templates
Path Description
inclusion_tags/paginator.html Template for rendering pagination when listing a category content. Used by paginator templatetag.
404.html Used to show user-friendly HTTP 404 page.
500.html Used to show user-friendly HTTP 500 page.
base.html Not required, but convention in Django apps is that this is the base layout template.

As mentioned in Template fallback mechanisms, when finding the suitable template, Ella uses smart template fallback for category, object and box templates so that the ones listed above are only used as last resort. Please refer there for the details on how Ella decides which template to use.

Middleware

class ella.core.middleware.UpdateCacheMiddleware

Response-phase cache middleware that updates the cache if the response is cacheable.

Must be used as part of the two-part update/fetch cache middleware. UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE_CLASSES so that it’ll get called last during the response phase.

process_response(request, response)

Sets the cache, if needed.

class ella.core.middleware.FetchFromCacheMiddleware

Request-phase cache middleware that fetches a page from the cache.

Must be used as part of the two-part update/fetch cache middleware. FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE_CLASSES so that it’ll get called last during the request phase.

process_request(request)

Checks whether the page is already cached and returns the cached version if available.

Related finders

ella.core.related_finders.related_by_category(obj, count, collected_so_far, mods=[], only_from_same_site=True)

Returns other Publishable objects related to obj by using the same category principle. Returns up to count objects.

Returns objects related to obj up to count by searching Related instances for the obj.

Common gotchas & FAQs

This section tries to present frequent problems Ella users run into. It provides solutions for those problems. Hopefully, it will enhance you understanding for doing the things Ella-way.

Creating site navigation

A common situation. Site needs a global menu which consists of links to categories and/or static pages. The question is: how do we do such a thing in Ella? The answer is suprisingly simple. Use positions mate! Using positions, it’s very simple to implement a site menu that can be edited via admin interface and fast as lightning thanks to caching mechanisms. Have a look at following template:

<!-- in position called "sitemenu" -->
{% load my_tags cache %}

{% cache CACHE_TIMEOUT "mainmenu" request.META.PATH_INFO %}
<div id="menu">
    <ul>
        <li class='home'><a href="/" title="Homepage">Homepage</a></li>

        {% render_menu_part "news" %}
        {% render_menu_part "economics" %}
        {% render_menu_part "regional" %}
    </ul>
</div>
{% endcache %}

This snippet can be in position’s text field (so it will be considered a raw html) and renders two-level site menu with highlighting what is currently active. It does so by using category in context and the responsible code for this is custom {% render_menu_part %} inclusion tag. New items can be added very easily. Each link is bound to a Category object using it’s slug.

@register.inclusion_tag('inc/menu_part.html', takes_context=True)
def render_menu_part(context, slug):
    category = context.get('category', None)
    root = Category.objects.get_by_tree_path(slug)

    return {
        'slug': slug,
        'root': root,
        'current_root': category.get_root_category() if category is not None else None,
        'current': category,
        'sub': root.get_children()
    }

Next, create the template for the menu part itself:

{% load my_tags %}
<li{% ifequal current.get_root_category root %} class="active"{% endifequal %}>
    <a href='{{ root.get_absolute_url }}'>{{ root.title }}</a>

    {% if sub %}
        <ul{% ifnotequal current_root root %} class="hidden"{% endifnotequal %}>
            {% for cat in sub %}
                <li{% ifequal current cat %} class="active"{% endifequal %}>
                    <a href="{{ cat.get_absolute_url }}">{{ cat.title }}</a>
                </li>
            {% endfor %}
        </ul>
    {% endif %}
</li>

The last step is to add position to the base template so that it will be shown on each page:

<!-- in page/base.html -->
{% load positions %}
...
{% block sitemenu %}
    {% ifposition sitemenu %}
        {% position sitemenu %}{% endposition %}
    {% endifposition %}
{% endblock %}
...

Category-specific sidebars

Sidebars to show information related to the currently active category. This can be a category-related poll, most interesting articles by editors choice or something completely different.

Answer for this question is the same as in previous question. Use positions! If you are not sure how to do it, please refer to Defining positions on the page.

Taking advantage of template inheritance

One of the best things about Django is undoubtedly it’s templating framework. And one of it’s most powerful features is the template inheritance. When building up Django templates, you use so-called blocks which can then be overriden in the child template which inherits from the parent template. When using Ella, it’s very beneficial to take the full advantage of the inheritance concept since the number of templates in large Ella websites can grow up significantly. Using the inheritance, you can very effectively write as little code as possible. Here are some hints you may found useful:

  • Always write the base template for object and category detail pages. Try to put there as much shared code as possible.

  • In you base template, don’t hesitate to define many blocks so the child templates won’t need to override big pieces of code.

  • It often make sense to put almost all parts of the base template into blocks. This applies for titles, descriptions, perexes, comment sections, object tools and so on. The more you allow to override, the smaller the child template will be.

  • When dealing with content used in more than one template type or when it’s hard to keep the inheritance scheme for some reason, use {% include %} and put those pieces of content in reusable snippets in your inclusion_tags template subdirectory. A common use case scenario for this can be a right sidebar. Consider following example of reusable right col template. First, define the base rightcol.html template:

    <!-- in page/inclusion_tags/rightcol.html -->
    {% block rightcol_top %}{% endblock %}
    
    {% block rightcol_top_articles %}
        ... code showing top articles ..
    {% endblock %}
    
    {% block rightcol_category_poll %}
        ... code showing poll related to the category ...
    {% endblock %}
    
    {% block rightcol_bottom %}{% endblock %}
    

    This template contains the the stuff a sidebar can contain with possibility to add your own stuff to the top and to the bottom of it. Next define child templates of the rightcol.html for homepage and common category:

    <!-- in page/inclusion_tags/rightcol_hp.html -->
    {% extends "page/inclusion_tags/rightcol.html" %}
    
    {% block rightcol_top %}
        ... some special code to add on top of the right col ...
    {% endblock %}
    
    <!-- in page/inclusion_tags/rightcol_category.html -->
    {% extends "page/inclusion_tags/rightcol.html" %}
    
    {% block rightcol_top_articles %}{% endblock %}
    

    For the purpose of this example, we added a special piece of code at the top of the sidebar in homepage and turned off the displaying of top articles in common categories. In real-world situation, your intents will be probably little different, but for the need of demonstration, this is sufficent. Finally, put following piece of code in your base category.html:

    <!-- in page/category.html -->
    {% block rightcol %}
        <div id="sidebar">
            {% block rightcol_content %}
                {% if is_homepage %}
                    {% include "inc/rightcol_hp.html" %}
                {% else %}
                    {% include "inc/rightcol_category.html" %}
                {% endif %}
            {% endblock %}
        </div>
    {% endblock %}
    

    In the main category template, we can easily implement a default behavior without need to duplicate a single line of code.

  • Every time you find yourself duplicating a HTML code, try to think if some kind of inheritance wouldn’t help you avoid doing so. In most cases, this would be true.

Static pages that don’t ever change

Content-heavy websites usually don’t build it’s success on lot of static pages. However, there are always some a Ella is ready to provide effective weapons to get rid of them.

The key here is to use Category and define a custom template, e.g. staticpage.html to use when you need to use the category as static page. The reasoning for this is that categories already have nice, SEO-friendly URLs and it would simply be unnecessary overhead to create a special solution for this. You can put you content to Category.content field which was added for that purpose. Then simply use something like this:

<!-- in page/staticpage.html -->
{% extends "page/base.html" %}

{% block content %}
    <h1>{{ category }}</h1>
    {% render category.content %}
{% endblock %}

Integrating searching

TODO: create ella-haystack and be done with it.

List of configuration settings

Core settings

CACHE_TIMEOUT

Timeout used for cache persistence for most of cached function Ella uses.

Default: 600

CACHE_TIMEOUT_LONG

Chache persistence timeout for rarely-changing results.

Default: 3600

CATEGORY_LISTINGS_PAGINATE_BY

Number of objects per page when browsing the category listing.

Default: 20

RSS_NUM_IN_FEED

Number of items in automated category RSS/Atom feeds.

Default: 10

RSS_ENCLOSURE_PHOTO_FORMAT

Photo format to use when providing photo link in RSS/Atom feed <enclosure> element.

Default: None

DOUBLE_RENDER

Boolean that switches the Double render function, for more details on Double rendering, see Double rendering.

Default: False

DOUBLE_RENDER_EXCLUDE_URLS

URLs to be excluded from double rendering. For details, see Double rendering.

Default: None

RELATED_FINDERS

List of named related finders. For instructions how to use it, see Working with related objects.

Default:

RELATED_FINDERS = {
    'default': (
        'ella.core.related_finders.directly_related',
        'ella.core.related_finders.related_by_category',
    ),
    'directly': (
        'ella.core.related_finders.directly_related',
    )
}

Photos settings

PHOTOS_FORMAT_QUALITY_DEFAULT

Sampling quality choices to use in administration when defining photo formats.

Default:

PHOTOS_FORMAT_QUALITY = (
    (45, _('Low')),
    (65, _('Medium')),
    (75, _('Good')),
    (85, _('Better')),
    (95, _('High')),
)
PHOTOS_CUSTOM_SUBDIR

Custom subdirectory in photos directory where to place the photos.

Default: ''

PHOTOS_UPLOAD_TO

Completely override where the photos are uploaded to. Symbols %Y, %m and %d are replaced by integer value of year, month and day respectively.

Default: 'photos/%Y/%m/%d'

Sites using Ella

Ella is getting more and more popular. Here are some sites that take advantage of it:

Community

License

Ella is licensed under the BSD licence. It utilizes many conceps and examples from django itself, djangosnippets and several other open-source project. We would like to thank the community around Django for the huge amount of great quality code they share with other Djangonauts. We are proud to be part of that community and hope that somebody will find this project helpfull.