Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

WHO AM I? Founder and Lead developer @NephilaIt django CMS core developer django CMS installer author

Slide 3

Slide 3 text

DJANGO CMS BEST PRACTICES django CMS applications are just Django applications With a twist

Slide 4

Slide 4 text

DJANGO CMS BEST PRACTICES WHY? Writing Django applications is easy Writing effective and clean Django applications requires some effort

Slide 5

Slide 5 text

DJANGO CMS BEST PRACTICES WHAT'S THAT? Guidelines being developed by the core team Community feedback My experience

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

DJANGO CMS BEST PRACTICES CAVEATS Nowhere near comprehensive Still in flux Task for 2015: Define the best practices Code hooks into django CMS core

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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)

Slide 10

Slide 10 text

DJANGO CMS BEST PRACTICES THE DETAILS Templates inheritance Namespace configuration django CMS Frontend editor django CMS Toolbar Meta tags Haystack integration

Slide 11

Slide 11 text

DJANGO CMS BEST PRACTICES LET'S START We will use djangocms_blog as a base List of posts Post detail view Tags Categories Comments ...

Slide 12

Slide 12 text

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 %}
{% block content_blog %}{% endblock %}
{% endblock content %}

Slide 13

Slide 13 text

DJANGO CMS BEST PRACTICES TEMPLATE RULES Extends CMS_TEMPLATE variable Define a conventional block each application should redefine We'll see the meta_mixin/meta.html include later

Slide 14

Slide 14 text

DJANGO CMS BEST PRACTICES TEMPLATES LAYOUT Divide application template from plugin ones

Slide 15

Slide 15 text

DJANGO CMS BEST PRACTICES TEMPLATES LAYOUT Use includes to share the common code
{% for post in posts_list %} {% include "djangocms_blog/includes/blog_item.html" with post=post {% empty %}

{% trans "No article found." %}

{% endfor %}

Slide 16

Slide 16 text

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'^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_matc

Slide 17

Slide 17 text

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)

Slide 18

Slide 18 text

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)

Slide 19

Slide 19 text

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'))

Slide 20

Slide 20 text

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:posts-latest' def get_paginate_by(self, queryset): return self.app_config.paginate_by

Slide 21

Slide 21 text

DJANGO CMS BEST PRACTICES IN THE ADMIN Use ModelAppHookConfig to get options values (e.g. in admin forms) class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin PlaceholderAdminMixin, TranslatableAdmin, ModelAppHookConf admin.ModelAdmin): app_config_values = { 'default_published': 'publish' }

Slide 22

Slide 22 text

DJANGO CMS BEST PRACTICES IN THE TEMPLATE Access the related namespace instance config through the current model {% if post.app_config.use_placeholder %}
{% render_placeholder post.content %} {% else %}
{% render_model post "post_text" "post_text" {% endif %}

Slide 23

Slide 23 text

DJANGO CMS BEST PRACTICES DJANGO CMS FRONTEND EDITOR

Slide 24

Slide 24 text

DJANGO CMS BEST PRACTICES DJANGO CMS FRONTEND EDITOR Base for plugins behaviour A wrapper around standard Django admin Available for all Django applications

Slide 25

Slide 25 text

DJANGO CMS BEST PRACTICES USE IT! Add FrontendEditableAdminMixin class PostAdmin(FrontendEditableAdminMixin, PlaceholderAdminMixin, TranslatableAdmin, admin.ModelAdmin): model = Post frontend_editable_fields = ('title', 'abstract', 'post_text') ...

Slide 26

Slide 26 text

DJANGO CMS BEST PRACTICES USE IT! Add template magic

{% render_model post "title" %}

{% render_model post "post_text" "post_text" %}
Edit the whole post Or just some fields

Slide 27

Slide 27 text

DJANGO CMS BEST PRACTICES USE IT!

Slide 28

Slide 28 text

DJANGO CMS BEST PRACTICES POWER TO THE USERS Toolbar is the main django CMS UI

Slide 29

Slide 29 text

DJANGO CMS BEST PRACTICES POWER TO THE USERS Available to all Django applications

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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)

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

DJANGO CMS BEST PRACTICES SHARE THE PASSION! Content is dumb without metadata schema.org OpenGraph ... Metadata defines the meaning of the content

Slide 35

Slide 35 text

DJANGO CMS BEST PRACTICES DJANGO CMS HAS GOT THAT COVERED! django-meta / django-meta-mixin Django applications to format document metadata: title author image ...

Slide 36

Slide 36 text

DJANGO CMS BEST PRACTICES AN EXAMPLE Define an attribute that maps metadata properties to attribute/function/callable Include a template ... That's all

Slide 37

Slide 37 text

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):

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

DJANGO CMS BEST PRACTICES THE TEMPLATE Templates are even easier ... {% include "meta_mixin/meta.html" %} ...

Slide 40

Slide 40 text

DJANGO CMS BEST PRACTICES IT'S DONE

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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=

Slide 43

Slide 43 text

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'

Slide 44

Slide 44 text

DJANGO CMS BEST PRACTICES CONFUSED? It may looks complex at first Have a look at other examples aldryn-newsblog djangocms-blog aldryn-events

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

GRAZIE!

Slide 47

Slide 47 text

QUESTIONS?