4. DJANGO CMS BEST PRACTICES
WHY?
Writing Django applications is easy
Writing effective and clean Django applications
requires some effort
5. DJANGO CMS BEST PRACTICES
WHAT'S THAT?
Guidelines being developed by the core team
Community feedback
My experience
6. 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
7. DJANGO CMS BEST PRACTICES
CAVEATS
Nowhere near comprehensive
Still in flux
Task for 2015: Define the best practices Code hooks
into django CMS core
8. DJANGO CMS BEST PRACTICES
YET ANOTHER BLOG
EXAMPLE
Standard blog features
Pluggable onto multiple pages with different
configurations
Integration with social networks
Integration with full-text search
9. DJANGO CMS BEST PRACTICES
THE BASICS
always 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)
10. DJANGO CMS BEST PRACTICES
THE DETAILS
Templates inheritance
Namespace configuration
django CMS Frontend editor
django CMS Toolbar
Meta tags
Haystack integration
11. DJANGO CMS BEST PRACTICES
LET'S START
We will use djangocms_blog as a base
List of posts
Post detail view
Tags
Categories
Comments
...
12. DJANGO CMS BEST PRACTICES
TEMPLATES INHERITANCE
Use django CMS page templates!
{% extends CMS_TEMPLATE %}
{% block meta %}
{% if meta %}
{% include "meta_mixin/meta.html" %}
{% endif %}
{% endblock meta %}
{% block content %}
<div class="app appblog span8">
{% block content_blog %}{% endblock %}
</div>
{% endblock content %}
13. DJANGO CMS BEST PRACTICES
TEMPLATE RULES
Extends CMS_TEMPLATEvariable
Define a conventional blockeach application
should redefine
We'll see the meta_mixin/meta.htmlinclude
later
14. DJANGO CMS BEST PRACTICES
TEMPLATES LAYOUT
Divide application template from plugin ones
15. DJANGO CMS BEST PRACTICES
TEMPLATES LAYOUT
Use includes to share the common code
<div class="plugin pluginblog"><div class="bloglatestentries">
{% for post in posts_list %}
{% include "djangocms_blog/includes/blog_item.html" with post=post
{% empty %}
<p class="blogempty">{% trans "No article found." %}</p>
{% endfor %}
</div></div>
16. DJANGO CMS BEST PRACTICES
NAMESPACE SUPPORT
Django built in support:
from django.conf.urls import include, patterns, url
urlpatterns = patterns('',
url(r'^blog/', include('djangocms_blog.urls', namespace='blog1'
url(r'^otherblog/', 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_matc
17. DJANGO CMS BEST PRACTICES
DJANGO CMS SUPPORT
django CMS just uses Django's own
class BlogApp(CMSApp):
name = _('Blog')
urls = ['djangocms_blog.urls']
app_name = 'djangocms_blog'
apphook_pool.register(BlogApp)
18. 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):
pass
setup_config(NewsBlogConfigForm, NewsBlogConfig)
19. DJANGO CMS BEST PRACTICES
ALDRYN-APPHOOKS-
CONFIG
Integrated with django CMS namespace instance
creation
Definition of per-namespace instance options
Tied to each model instance
class Post(ModelMeta, TranslatableModel):
...
app_config = models.ForeignKey(BlogConfig,
verbose_name=_('app. config'))
20. DJANGO CMS BEST PRACTICES
IN VIEWS
AppConfigMixin:
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:postslatest'
def get_paginate_by(self, queryset):
return self.app_config.paginate_by
21. DJANGO CMS BEST PRACTICES
IN THE ADMIN
Use ModelAppHookConfigto get options values
(e.g. in admin forms)
class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin
PlaceholderAdminMixin, TranslatableAdmin, ModelAppHookConf
admin.ModelAdmin):
app_config_values = {
'default_published': 'publish'
}
22. DJANGO CMS BEST PRACTICES
IN THE TEMPLATE
Access the related namespace instance config through
the current model
{% if post.app_config.use_placeholder %}
<div class="blogcontent">{% render_placeholder post.content %}
{% else %}
<div class="blogcontent">{% render_model post "post_text" "post_text"
{% endif %}
25. DJANGO CMS BEST PRACTICES
DJANGO CMS FRONTEND
EDITOR
Base for plugins behaviour
A wrapper around standard Django admin
Available for all Django applications
26. DJANGO CMS BEST PRACTICES
USE IT!
Add FrontendEditableAdminMixin
class PostAdmin(FrontendEditableAdminMixin,
PlaceholderAdminMixin, TranslatableAdmin,
admin.ModelAdmin):
model = Post
frontend_editable_fields = ('title', 'abstract', 'post_text')
...
27. DJANGO CMS BEST PRACTICES
USE IT!
Add template magic
<h2>{% render_model post "title" %}</h2>
<div class="blogcontent">
{% render_model post "post_text" "post_text" %}
</div>
Edit the whole post
Or just some fields
29. DJANGO CMS BEST PRACTICES
POWER TO THE USERS
Toolbar is the main django CMS UI
30.
31. DJANGO CMS BEST PRACTICES
POWER TO THE USERS
Available to all Django applications
32.
33. DJANGO CMS BEST PRACTICES
HOW TO IMPLEMENT IT
Toolbar is very rich and powerful
I'll just show the basics
Check documentation for details
http://django-cms.rtfd.org/en/develop/how_to/toolbar.html
34. DJANGO CMS BEST PRACTICES
HOW TO IMPLEMENT IT
Create cms_toolbar.py
Define the links to the admin views
@toolbar_pool.register
class 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)
35. DJANGO CMS BEST PRACTICES
HOW TO IMPLEMENT IT
Add 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_objec
return context
36. DJANGO CMS BEST PRACTICES
SOME NOTES
Redirect 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
37. DJANGO CMS BEST PRACTICES
SHARE THE PASSION!
Content is dumb without metadata
schema.org
OpenGraph
...
Metadata defines the meaning of the content
38. DJANGO CMS BEST PRACTICES
DJANGO CMS HAS GOT
THAT COVERED!
django-meta/ django-meta-mixin
Django applications to format document metadata:
title
author
image
...
39. DJANGO CMS BEST PRACTICES
AN EXAMPLE
Define an attribute that maps metadata properties to
attribute/function/callable
Include a template
...
That's all
40. 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):
41. DJANGO CMS BEST PRACTICES
THE VIEW
as_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
42. DJANGO CMS BEST PRACTICES
THE TEMPLATE
Templates are even easier
<head>
...
{% include "meta_mixin/meta.html" %}
...
</head>
43. DJANGO CMS BEST PRACTICES
IT'S DONE
<meta property="og:title" content="L'evento Djangobeer a Firenze, organizz
<meta property="og:url" content="http://www.nephila.it/it/blog/2014/09/18/
<meta property="og:description" content="La prima Djangobeer a Firenze: co
<meta property="og:image" content="http://www.nephila.it/media/filer_publi
<meta property="og:type" content="Article">
<meta property="article:published_time" content="20140918 18:03:20"
<meta property="article:modified_time" content="20141224 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, organiz
<meta name="twitter:url" content="http://www.nephila.it/it/blog/2014/09/18
<meta name="twitter:description" content="La prima Djangobeer a Firenze: c
<meta name="twitter:image:src" content="http://www.nephila.it/media/filer_
<meta name="twitter:creator" content="@NephilaIt">
<meta name="twitter:site" content="@nephilait">
<link rel="author" href="https://plus.google.com/+NephilaIt"/>
44. DJANGO CMS BEST PRACTICES
THE IMPORTANCE OF BEING
INDEXED
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
45. DJANGO CMS BEST PRACTICES
HOW TO CREATE THE INDEX
search_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=
46. 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'
47. DJANGO CMS BEST PRACTICES
CONFUSED?
It may looks complex at first
Have a look at other examples
aldryn-newsblog
djangocms-blog
aldryn-events
48. 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