Saturday, August 26, 2017

Django: efficient template/string separation and override

Leave a Comment

I have a generic Django view that renders a template. The template is in an app which other projects will use. Importing projects will typically subclass the View the app provides. The View has a default template, which does a job with generic wording.

99% of the time, subclassing Views will want to only change the text, so rather than make them duplicate the template for the sake of altering non-markup wording, i'm looking for a way to allow users of the class to replace wording in the template in the most efficient way.

Options explored so far:

  • template partials containing only the text which using apps can override (magic, a lot of user work)
  • A template_strings method on the view which provides a dict of strings which end up in the template context which subclasses can override
  • Using (abusing?) the translation system such that the app provides default english translations and using code can provide their own translations instead (not actually worked this one out yet, just an idea)
  • Doing the above template_strings through AppConfig, but this seems ... yucky like it may get very unweildy with a lot of English strings. If doing this I would create a context-like setup so you don't have to re-declare all strings

Seems like it should be a solved problem to subclass a view which does a complete job and just provide alternate strings for text. Is there a better method than the above? Convention? Something I am missing?

(django 1.11 Python 3.6.2)

3 Answers

Answers 1

You can either inherit TemplateView or add ContextMixin to your view, and then override the get_context_data function like this:

from django.views.generic import TemplateView  class BaseView(TemplateView):     template_name = "common.html"  class SubView(BaseView):     def get_context_data(self, **kwargs):         context = super(SubView, self).get_context_data(**kwargs)         context['content'] = "Some sub view text"         return context 

Update: Use template overriding

If you want to separate the text out, this is the better way to go To allow easily and DRY override template across apps, you might need to install this package (Some other detail here)

We define it similarly as above, but change the template_name instead:

from django.views.generic import TemplateView      class BaseView(TemplateView):         template_name = "main.html"      # on another app     class SubView(BaseView):         template_name = "sub_view.html" 

Then the magic is you can extends and override block of the BaseView template like this:

base_app/templates/main.html

<p>I'm Common Text</p> {% block main %}   <p>I'm Base View</p> {% endblock %} 

sub_app/templates/sub_view.html

{% extends "base_app:main.html" %} {% block main %}   <p>I'm Sub View</p> {% endblock %} 

The result would be:

<p>I'm Common Text</p> <p>I'm Sub View</p> 

Answers 2

Assuming your view is a subclass of TemplateView, you could define the strings in your views get_context_data method. That would let developers that are subclasses your view overwrite the strings in get_context_data. This is similar to you're template_strings option but more idiomatic since it's leveraging the get_context_data method from Django.

Answers 3

Afaik you covered the options pretty well. My example is probably just a variant of the the template strings but maybe it helps anyway...

class DefaultStringProvider():     TITLE = 'Hello'     DESCRIPTION = 'Original description'     CATEGORY = 'Stuff'  class MyTemplateBaseView(TemplateView):     def get_context_data(self, **kwargs):         return super(MyTemplateBaseView, self).get_context_data(             provider=self.get_string_provider(), **kwargs)      def get_string_provider(self):         return DefaultStringProvider()  class OtherView(MyTemplateBaseView):     template_name = 'welcome.html'      def get_string_provider(self):         p = DefaultStringProvider()         p.TITLE = 'Hello'         p.DESCRIPTION = 'New description'         return p 

The idea is to have a default string provider and the base view populates the context with it through get_string_provider().

It will at least be quite clear which strings can be overridden for a user extending the base class and it will not interfere with translations.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment