50
DJANGO MEETUP - SKILLS MATTER DJANGO CMS BEST PRACTICES by Iacopo Spalletti @yakkys

Django cms best practices

Embed Size (px)

Citation preview

Page 1: Django cms best practices

DJANGO MEETUP - SKILLS MATTER

DJANGO CMSBEST PRACTICES

by

Iacopo Spalletti

@yakkys

Page 2: Django cms best practices

WHO AM I?Founder and Lead developer @NephilaIt

django CMS core developer 

django CMS installer author

Page 3: Django cms best practices

DJANGO CMS

BEST PRACTICESdjango CMS applications are just Django applications

With a twist

Page 4: Django cms best practices

DJANGO CMS BEST PRACTICES

WHY?Writing Django applications is easy

Writing effective and clean Django applications

requires some effort

Page 5: Django cms best practices

DJANGO CMS BEST PRACTICES

WHAT'S THAT?Guidelines being developed by the core team

Community feedback

My experience

Page 6: Django cms best practices

DJANGO CMS BEST PRACTICES

WHAT FOR?Providing:

DRY

Reusability

Tight integration with on-site and off-site services (full-

text search, social networks ...)

Principle of least surprise

Page 7: Django cms best practices

DJANGO CMS BEST PRACTICES

CAVEATSNowhere near comprehensive

Still in flux

Task for 2015: Define the best practices Code hooks

into django CMS core

Page 8: Django cms best practices

DJANGO CMS BEST PRACTICES

YET ANOTHER BLOGEXAMPLE

Standard blog features

Pluggable onto multiple pages with different

configurations

Integration with social networks

Integration with full-text search

Page 9: Django cms best practices

DJANGO CMS BEST PRACTICES

THE BASICSalways define cms_app.py

never plug the application in urlconf directly

prefer translatable models using django-parler

use tests :)

follow code conventions

(don't follow this talk code style)

Page 10: Django cms best practices

DJANGO CMS BEST PRACTICES

THE DETAILSTemplates inheritance

Namespace configuration

django CMS Frontend editor

django CMS Toolbar

Meta tags

Haystack integration

Page 11: Django cms best practices

DJANGO CMS BEST PRACTICES

LET'S STARTWe will use djangocms_blog as a base

List of posts

Post detail view

Tags

Categories

Comments

...

Page 12: Django cms best practices

DJANGO CMS BEST PRACTICES

TEMPLATES INHERITANCEUse django CMS page templates!

{% extends CMS_TEMPLATE %}

{% block meta %}    {%  if meta %}        {% include "meta_mixin/meta.html" %}    {% endif %}{% endblock meta %}

{% block content %}<div class="app app­blog span8">    {% block content_blog %}{% endblock %}</div>{% endblock content %}

Page 13: Django cms best practices

DJANGO CMS BEST PRACTICES

TEMPLATE RULESExtends CMS_TEMPLATE variable

Define a conventional block each applicationshould redefine

We'll see the meta_mixin/meta.html include

later

Page 14: Django cms best practices

DJANGO CMS BEST PRACTICES

TEMPLATES LAYOUTDivide application template from plugin ones

Page 15: Django cms best practices

DJANGO CMS BEST PRACTICES

TEMPLATES LAYOUTUse includes to share the common code

<div class="plugin plugin­blog"><div class="blog­latest­entries">    {% for post in posts_list %}        {% include "djangocms_blog/includes/blog_item.html" with post=post TRUNCWORDS_COUNT=TRUNCWORDS_COUNT %}    {% empty %}        <p class="blog­empty">{% trans "No article found." %}</p>    {% endfor %}    </div></div>

Page 16: Django cms best practices

DJANGO CMS BEST PRACTICES

NAMESPACE SUPPORTDjango built in support:

from django.conf.urls import include, patterns, url

urlpatterns = patterns('',    url(r'̂blog/', include('djangocms_blog.urls', namespace='blog1'    url(r'̂other­blog/', include('djangocms_blog.urls', namespace='blog2')

my_url = reverse('djangocms_blog:index', current_app='blog1')my_url = reverse('djangocms_blog:index', current_app=request.resolver_match.namespace)

Page 17: Django cms best practices

DJANGO CMS BEST PRACTICES

DJANGO CMS SUPPORTdjango CMS just uses Django's own

class BlogApp(CMSApp):    name = _('Blog')    urls = ['djangocms_blog.urls']    app_name = 'djangocms_blog'

apphook_pool.register(BlogApp)

Page 18: Django cms best practices

DJANGO CMS BEST PRACTICES

NEW IN 3.1!Enter aldryn-apphooks-config

class BlogConfig(TranslatableModel, AppHookConfig):    paginate_by = models.PositiveIntegerField(        _('Paginate size'),        blank=False,        default=5,    )    ...

class NewsBlogConfigForm(AppDataForm):    passsetup_config(NewsBlogConfigForm, NewsBlogConfig)

Page 19: Django cms best practices

DJANGO CMS BEST PRACTICES

ALDRYN-APPHOOKS-CONFIG

Integrated with django CMS namespace instancecreation

Definition of per-namespace instance options

Tied to each model instanceclass Post(ModelMeta, TranslatableModel):   ...   app_config = models.ForeignKey(BlogConfig,        verbose_name=_('app. config'))

Page 20: Django cms best practices

DJANGO CMS BEST PRACTICES

IN VIEWSAppConfigMixin:

sets up the namespace support

retrieves the current configuration

class PostListView(AppConfigMixin, ViewUrlMixin, ListView):    model = Post    context_object_name = 'post_list'    template_name = 'djangocms_blog/post_list.html'    view_url_name = 'djangocms_blog:posts­latest'

    def get_paginate_by(self, queryset):        return self.app_config.paginate_by

Page 21: Django cms best practices

DJANGO CMS BEST PRACTICES

IN THE ADMINUse ModelAppHookConfig to get options values

(e.g. in admin forms)

class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin                PlaceholderAdminMixin, TranslatableAdmin, ModelAppHookConfig                admin.ModelAdmin):

    app_config_values = {        'default_published': 'publish'    }

Page 22: Django cms best practices

DJANGO CMS BEST PRACTICES

IN THE TEMPLATEAccess the related namespace instance config through

the current model

{% if post.app_config.use_placeholder %}    <div class="blog­content">{% render_placeholder post.content %}{% else %}    <div class="blog­content">{% render_model post "post_text" "post_text" %}{% endif %}

Page 23: Django cms best practices

DJANGO CMS BEST PRACTICES

DJANGO CMS FRONTENDEDITOR

Page 24: Django cms best practices
Page 25: Django cms best practices

DJANGO CMS BEST PRACTICES

DJANGO CMS FRONTENDEDITOR

Base for plugins behaviour

A wrapper around standard Django admin

Available for all Django applications

Page 26: Django cms best practices

DJANGO CMS BEST PRACTICES

USE IT!Add FrontendEditableAdminMixin

class PostAdmin(FrontendEditableAdminMixin,                PlaceholderAdminMixin, TranslatableAdmin,                admin.ModelAdmin):    model = Post    frontend_editable_fields = ('title', 'abstract', 'post_text')    ...

Page 27: Django cms best practices

DJANGO CMS BEST PRACTICES

USE IT!Add template magic

<h2>{% render_model post "title" %}</h2>

<div class="blog­content">{% render_model post "post_text" "post_text" %}</div>

Edit the whole post

Or just some fields

Page 28: Django cms best practices

DJANGO CMS BEST PRACTICES

USE IT!

Page 29: Django cms best practices

DJANGO CMS BEST PRACTICES

POWER TO THE USERSToolbar is the main django CMS UI

Page 30: Django cms best practices
Page 31: Django cms best practices

DJANGO CMS BEST PRACTICES

POWER TO THE USERSAvailable to all Django applications

Page 32: Django cms best practices
Page 33: Django cms best practices

DJANGO CMS BEST PRACTICES

HOW TO IMPLEMENT ITToolbar is very rich and powerful

I'll just show the basics

Check documentation for detailshttp://django-cms.rtfd.org/en/develop/how_to/toolbar.html

Page 34: Django cms best practices

DJANGO CMS BEST PRACTICES

HOW TO IMPLEMENT ITCreate cms_toolbar.pyDefine the links to the admin views

@toolbar_pool.registerclass BlogToolbar(CMSToolbar):    watch_models = [Post]

    def populate(self):        if not self.is_current_app:            return        admin_menu = self.toolbar.get_or_create_menu(             'djangocms_blog', _('Blog'))        url = reverse('admin:djangocms_blog_post_changelist')        admin_menu.add_modal_item(_('Post list'), url=url)        url = reverse('admin:djangocms_blog_post_add')        admin_menu.add_modal_item(_('Add post'), url=url)

 

Page 35: Django cms best practices

DJANGO CMS BEST PRACTICES

HOW TO IMPLEMENT ITAdd contextual-aware links

current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER)if current_post:    admin_menu.add_modal_item(_('Edit Post'),        reverse(            'admin:djangocms_blog_post_change',                args=(current_post.pk,)        ), active=True)

class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView)    model = Post    def get_context_data(self, **kwargs):        context = super(PostDetailView, self).get_context_data(**kwargs)        setattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, self.get_object())        return context

Page 36: Django cms best practices

DJANGO CMS BEST PRACTICES

SOME NOTESRedirect browser to current post upon edit

class BlogToolbar(CMSToolbar):    watch_models = [Post]

Hide the application toolbar item when outside the

application URLs

def populate(self):    if not self.is_current_app:        return

Page 37: Django cms best practices

DJANGO CMS BEST PRACTICES

SHARE THE PASSION!Content is dumb without metadata

schema.org

OpenGraph

...

Metadata defines the meaning of the content

Page 38: Django cms best practices

DJANGO CMS BEST PRACTICES

DJANGO CMS HAS GOTTHAT COVERED!

django-meta / django-meta-mixin

Django applications to format document metadata:

title

author

image

...

Page 39: Django cms best practices

DJANGO CMS BEST PRACTICES

AN EXAMPLEDefine an attribute that maps metadata properties to

attribute/function/callable

Include a template

...

That's all

 

Page 40: Django cms best practices

DJANGO CMS BEST PRACTICES

THE MODEL_metadata = {    'title': 'get_title',    'description': 'get_description',    'og_description': 'get_description',    'image': 'get_image_full_url',    'object_type': get_setting('TYPE'),    ....}

def get_title(self):    title = self.safe_translation_getter('meta_title')    if not title:        title = self.safe_translation_getter('title')    return title.strip()

def get_image_full_url(self):

Page 41: Django cms best practices

DJANGO CMS BEST PRACTICES

THE VIEWas_meta() method does the magic

def get_context_data(self, **kwargs):    context = super(MyView, self).get_context_data(**kwargs)    context['meta'] = self.get_object().as_meta()    return context

Page 42: Django cms best practices

DJANGO CMS BEST PRACTICES

THE TEMPLATETemplates are even easier

<head>    ...    {% include "meta_mixin/meta.html" %}    ...</head>

Page 43: Django cms best practices

DJANGO CMS BEST PRACTICES

IT'S DONE<meta property="og:title" content="L'evento Djangobeer a Firenze, organizzato da Nephila"<meta property="og:url" content="http://www.nephila.it/it/blog/2014/09/18/djangobeer/"<meta property="og:description" content="La prima Djangobeer a Firenze: com'è nata l'idea, come si è svolta la serata e lo sviluppo di un evento, a partire dal logo. Un evento in cui la cultura della birra si unisce a quella dell'Open Source."<meta property="og:image" content="http://www.nephila.it/media/filer_public/76/1e/761ed2d2­cd6e­41db­ab72­e2e0c613f903/djangobeer.jpg"<meta property="og:type" content="Article"><meta property="article:published_time" content="2014­09­18 18:03:20"<meta property="article:modified_time" content="2014­12­24 11:44:15.769410"<meta name="twitter:domain" content="www.nephila.it"><meta name="twitter:card" content="Summary"><meta name="twitter:title" content="L'evento Djangobeer a Firenze, organizzato da Nephila"<meta name="twitter:url" content="http://www.nephila.it/it/blog/2014/09/18/djangobeer/"<meta name="twitter:description" content="La prima Djangobeer a Firenze: com'è nata l'idea, come si è svolta la serata e lo sviluppo di un evento, a partire dal logo. Un evento in cui la cultura della birra si unisce a quella dell'Open Source."<meta name="twitter:image:src" content="http://www.nephila.it/media/filer_public/76/1e/761ed2d2­cd6e­41db­ab72­e2e0c613f903/djangobeer.jpg"<meta name="twitter:creator" content="@NephilaIt"><meta name="twitter:site" content="@nephilait"><link rel="author" href="https://plus.google.com/+NephilaIt"/>

Page 44: Django cms best practices

DJANGO CMS BEST PRACTICES

THE IMPORTANCE OF BEINGINDEXED

What if I want to make my content indexable?

aldryn_search to the rescue

Easier binding between Haystack and your models

Knows a few things of django CMS

Page 45: Django cms best practices

DJANGO CMS BEST PRACTICES

HOW TO CREATE THE INDEXsearch_indexes.py

class PostIndex(get_index_base()):    def get_title(self, obj):        return obj.get_title()

    def index_queryset(self, using=None):        self._get_backend(using)        language = self.get_current_language(using)        filter_kwargs = self.get_index_kwargs(language)        qs = self.get_index_queryset(language)        if filter_kwargs:            return qs.translated(language, **filter_kwargs)        return qs

    def get_index_queryset(self, language):        return self.get_model().objects.active_translations(            language_code=language).filter(app_config__search_indexed=

Page 46: Django cms best practices

DJANGO CMS BEST PRACTICES

HOW TO CREATE THE INDEX#2

def get_search_data(self, language=None, request=None):        """        Provides an index for use with Haystack, or, for populating        Article.translations.search_data.        """        if not self.pk:            return ''        if language is None:            language = get_language()        if request is None:            request = get_request(language=language)        description = self.safe_translation_getter('lead_in')        text_bits = [strip_tags(description)]        for category in self.categories.all():            text_bits.append(                force_unicode(category.safe_translation_getter('name'

Page 47: Django cms best practices

DJANGO CMS BEST PRACTICES

CONFUSED?It may looks complex at first

Have a look at other examples

aldryn-newsblog

djangocms-blog

aldryn-events

 

Page 48: Django cms best practices

DJANGO CMS BEST PRACTICES

TO BE CONTINUED...There's more to come!

Content versioning support

Managing draft/live copy version of objects

Your suggestions

A detailed white paper in the next months

Page 49: Django cms best practices

GRAZIE!

Page 50: Django cms best practices

QUESTIONS?