Feature: Support a javascript template language on the server

287 views
Skip to first unread message

Emil Stenström

unread,
May 30, 2015, 12:52:34 PM5/30/15
to django-d...@googlegroups.com
Hi,

This is the second feature proposal as part of my general drive for
getting Django to work better for javascript heavy sites.

Support a javascript template language on the server
----------------------------------------------------

The multiple template engine work has made it possible to switch out
Django Templates with another engine. One of the most powerful things
this enables is to use a template language that is executable both on
the server and client.

A typical use-case for this could be that you have a list of tweets on
your site. When the user first loads the page you send the full HTML for
all the rendered tweets over. When there's a new tweet you would like to
just add that one tweet to the list, without re-rendering the whole
page. Now you have a couple of options:

1. Reimplement the Django template code from your site in javascript.
This is harder the more complex the template your have. Also risks bugs
when you change the server-side template but forget the client-side one.

2. Use Pjax, render everything on the server and send the result to the
page. This sends unnecessary data over the wire, and requires that you
figure out how to append the data in the correct location.

3. Send the server-side templates to the browser, and then re-render on
the client when new data arrives. This uses the least data over the
wire, and does not require that you keep track of mappings, just update
the data and re-render everything (React.js's virtual DOM can make this
really fast if you need it to be).

Option 3 opens up lots of interesting use-cases for Django, that
previously was only possible for javascript-based frameworks.

---

To be clear, I'm not saying that Django should include any front-end
code at all. That's something that the individual developer should have
full control over. But what needs to happen is that the same templates
that Django uses needs to be accessible to, and executable in, javascript.

For this to work, two things needs to be built:

1. A way to access the template and the template context for a view
separately, so that it can be delivered to the client. Maybe the easiest
would be to modify django.shortcuts.render so it returns both the input
and the result. The client could then get access to this via a <script>
tag when the page renders.

2. A template language that has a solid javascript implementation. To be
able to render the templates the server side and client side must use
the same language. I see two options here, either make sure the Jinja
flavor we are supporting works with JS templatations (maybe
https://mozilla.github.io/nunjucks/ ?), or get a handlebars
implementation working with Django (https://github.com/wbond/pybars3 ?).
Or maybe there's some third option I didn't think of...

---

This would open up great possibilities for Django, with sites that would
feel much more responsive than today. It would be possible to build
something like Meteor.js on top of Django, that propagates changes in
the database all the way to the correct DOM node in the browser.

Would anyone we willing to work with me on this? I think the changes to
django.shortcuts.render() is something that would have to happen in
Django, but the new template engine is something that could be done as a
separate project I guess.

Thoughts? Ideas?

(And yes, for people seeing the connections between this feature and
template components, you are right: they would be awesome together)

Regards,

Emil Stenström
Twitter: @EmilStenstrom

Enrique Paredes

unread,
May 31, 2015, 12:49:07 PM5/31/15
to django-d...@googlegroups.com, django-d...@googlegroups.com
IMHO, this can be easily solved with nunjucks.js and jinja which are both interchangeable, but in my experience it's better to had 2 template languages. Using only one tpl gives you the need to implement the same in the django views than in the js controller wich is "harsh" coupled and a PITA.

Also the need to reimplement django.shortcuts.render is unneded. Add in the base tpl a {% if request.is_ajax %} then wrap the result in a entire html tpl for regular http calls and the new content only in the other case.

Another option is to use django_braces and AjaxResponseView to respond back ajax with only json data but again you'll be force to sync django views and the controller for js.

Just my two cents, I'm used to build JS intense sites backed with django without any problem.

For me the work should be documenting how to use django with a client MV# and list the options and the apis for that.

Cheers!
E.








--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/5569EAC9.4040503%40kth.se.
For more options, visit https://groups.google.com/d/optout.


Emil Stenström

unread,
May 31, 2015, 6:21:21 PM5/31/15
to django-d...@googlegroups.com

On Sunday, 31 May 2015 18:49:07 UTC+2, Enrique Paredes wrote:
IMHO, this can be easily solved with nunjucks.js and jinja which are both interchangeable, but in my experience it's better to had 2 template languages. Using only one tpl gives you the need to implement the same in the django views than in the js controller wich is "harsh" coupled and a PITA.

I'm not sure what your argument is here.

Are you saying you prefer implementing all templates in two languages, since implementing views twice is too much work? The time you save being able to reuse the same templates in the client side is huge, and avoids the maintenance burden of keeping two identical things in sync.

I'm also not saying you should implement views twice. Each time you click a link that request can go to the exact same server side view, get the correct data back (only the data), and re-render only the parts that changed.
 
Also the need to reimplement django.shortcuts.render is unneded. Add in the base tpl a {% if request.is_ajax %} then wrap the result in a entire html tpl for regular http calls and the new content only in the other case.

I'm not sure I follow here. Are you saying I should just go through all my templates and split them in two? What do you mean by "new content" here? My suggestion is that you would send only the new DATA, and that the client would use that data, and the templates, to re-render the page.

The good thing about changing render is that render gets both the template and the template_context as input (the two things we need on the client side to re-render the page), all you need is a way to get them out of there. Maybe this is possible to get from a HTTPResponse, if it is, no changes would have to be made to render().

Another option is to use django_braces and AjaxResponseView to respond back ajax with only json data but again you'll be force to sync django views and the controller for js.

Just my two cents, I'm used to build JS intense sites backed with django without any problem.

For me the work should be documenting how to use django with a client MV# and list the options and the apis for that.

I'm not advocating client side MVC here, I think most sites would be better of just letting Django handle all the routing. That's why the idea of just modifying your existing views to return the template_context is so convenient. All you need on the client side is to turn link clicks to ajax calls (or fallback to normal clicks if the client doesn't support javascript), and render templates with the returned data.

From your different suggestions throughout your e-mail it sounds to me you have had to duplicate lots of your code between the server and the client. This proposal would avoid much of that.

Tom Evans

unread,
Jun 1, 2015, 5:57:42 AM6/1/15
to django-d...@googlegroups.com
ISTM that django already provides all these "options" (you are clearly
slanting the view that "options" 1 and 2 are rubbish and only option 3
is a the valid choice..). Pluggable template engines landed in 1.8,
read here for extensive details:

https://myks.org/en/multiple-template-engines-for-django/

With pluggable template engines, is there anything else required in
Django core to plug in whatever template language your project
requires?

Cheers

Tom

Javier Guerra Giraldez

unread,
Jun 1, 2015, 12:09:00 PM6/1/15
to django-d...@googlegroups.com
On Sun, May 31, 2015 at 5:21 PM, Emil Stenström <e...@kth.se> wrote:
> Are you saying you prefer implementing all templates in two languages, since
> implementing views twice is too much work? The time you save being able to
> reuse the same templates in the client side is huge, and avoids the
> maintenance burden of keeping two identical things in sync.
>
> I'm also not saying you should implement views twice. Each time you click a
> link that request can go to the exact same server side view, get the correct
> data back (only the data), and re-render only the parts that changed.
>
>>
>> Also the need to reimplement django.shortcuts.render is unneded. Add in
>> the base tpl a {% if request.is_ajax %} then wrap the result in a entire
>> html tpl for regular http calls and the new content only in the other case.
>
>
> I'm not sure I follow here. Are you saying I should just go through all my
> templates and split them in two? What do you mean by "new content" here? My
> suggestion is that you would send only the new DATA, and that the client
> would use that data, and the templates, to re-render the page.


first advice when discussing things on the internet: don't take it personal.

I think that Enrique means that having "the same" template language is
a non-goal. the "cognitive burden" of two languages is not so heavy;
and since these two environments are so different, there would be
different considerations that make some features unpractical on one
side or the other; or maybe some choice could be "the best" on one and
too awkward or badly supported on the other.

personally, i mostly agree: i do use templating on JS, and it doesn't
look at all like Django templates. That means sometimes i render some
things in the server, other things in the front end, and a few things
(very few) on both, leading to mostly duplicate code. would it be
nice to have some code that works equally well on either side? yes,
but it's a relatively small gain.

if "using the same language" was so important, we would all be using node.js...

--
Javier

Bobby Mozumder

unread,
Jun 1, 2015, 11:19:43 PM6/1/15
to django-d...@googlegroups.com
At this point it’s probably easiest for Django to provide templates only for Javascript front-end, and for Django to only serve API endpoints.

We really don’t need Django to serve HTML pages anymore, except for the initial page-load.

I believe this is where the web is headed for the long-term.

It may be possible to develop a Python syntax that analogous to HTML templates, with Django translating that for Javascript templating.

Now, whether Django serves these templates in Angular/Ember/React/whatever or develop some new framework (or even new HTML standards that operates on APIs directly...), is another question.

-bobby
> --
> You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
> To post to this group, send email to django-d...@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-developers.
> To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/CAFkDaoSOYOoyX0w_m0ERzhiJmVPXWGr6pB29AongesG43gOw1w%40mail.gmail.com.

Emil Stenström

unread,
Jun 2, 2015, 2:38:10 AM6/2/15
to django-d...@googlegroups.com
On Monday, 1 June 2015 18:09:00 UTC+2, Javier Guerra wrote:
I think that Enrique means that having "the same" template language is
a non-goal.  the "cognitive burden" of two languages is not so heavy;

I really don't agree with this. Think of beginners learning how to build a modern website. Either they learn both Django templates and handlebars.js, or just ONE language. Of course the cognitive burden is much heavier than learning only one language.
 
and since these two environments are so different, there would be
different considerations that make some features unpractical on one
side or the other; or maybe some choice could be "the best" on one and
too awkward or badly supported on the other.

I don't think the environments are so different. Unless you are breaking best practices and doing ORM calls in your custom template tags the point of both languages are the same: Take a dictionary and turn it into HTML by looping and including subtemplates. It's a very similar problem.
 
personally, i mostly agree: i do use templating on JS, and it doesn't
look at all like Django templates.  That means sometimes i render some
things in the server, other things in the front end, and a few things
(very few) on both, leading to mostly duplicate code.  would it be
nice to have some code that works equally well on either side? yes,
but it's a relatively small gain.
 
Having one language would make things like this much easier. And as with must easy to use things, you would likely use it more often, leading to sites that are more responsive and easier to use.

if "using the same language" was so important, we would all be using node.js...
 
Did you see Geoff Schmids presentation on Meteor.js on DjangoCon 2012? https://www.youtube.com/watch?v=34_oKFx43O4

People are switching to JS just because of "isomorphism", being able to avoid duplicate code.

Emil Stenström

unread,
Jun 2, 2015, 2:43:02 AM6/2/15
to django-d...@googlegroups.com
On Tuesday, 2 June 2015 05:19:43 UTC+2, Bobby Mozumder wrote:
At this point it’s probably easiest for Django to provide templates only for Javascript front-end, and for Django to only serve API endpoints.

We really don’t need Django to serve HTML pages anymore, except for the initial page-load.

In 2012, Twitter posted in their engineering blog about their conclusions of trying to move all rendering to the client. It was just too slow. The answer, and what is considered "best practice" now, is to render everything on the server the first load, and render all subsequent page loads to the client. This also means you get a nice fallback for clients that don't support javascript.

Andrew Ingram

unread,
Jun 2, 2015, 6:58:22 AM6/2/15
to django-d...@googlegroups.com
Based on my own experiences building isomorphic JavaScript apps with Django, I haven't had any issues where I felt Django was getting in my way. I've tackled the problem in two different ways, both of which worked without any great difficulty:

1. The primary web-app is actually a node process. Everything you'd consider a view is handled by React and React-Router, data requirements are handled by making an HTTP request to Django REST Framework. This isn't especially efficient, since the base response times aren't great when you're depending on HTTP for data (this can obviously be improved by getting the data via other channels).
2. Use node purely as a rendering engine. I actually created an alternative to TemplateResponseMixin that communicated with a node process via zerorpc (chosen for the sake of simplicity) rather than rendering a template from disk. This abstraction could be made neater by packaging it up as a custom template engine that takes care of converting the data into a format that zerorpc accepts (ie no querysets or rich objects). This approach was significantly faster with base response times of about 8ms for a simple page with no data dependencies, but I felt I was implementing my routing logic twice (once in Django, once in node).

The second approach is easier to get running at acceptable performance levels quickly, but is quite clunky to implement. Going forward I'm probably going to favour the first approach, which essentially means turning my Django projects into nothing more than a data layer, which is fine for me because I've found that once you're using React (or similar) heavily, you're not using much else from Django anyway.

Andy



--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Aymeric Augustin

unread,
Jun 2, 2015, 7:54:44 AM6/2/15
to django-d...@googlegroups.com
Hello Emil,

2015-05-30 17:52 GMT+01:00 Emil Stenström <e...@kth.se>:
But what needs to happen is that the same templates that Django uses needs to be accessible to, and executable in, javascript.

For this to work, two things needs to be built:

1. A way to access the template and the template context for a view separately, so that it can be delivered to the client. Maybe the easiest would be to modify django.shortcuts.render so it returns both the input and the result. The client could then get access to this via a <script> tag when the page renders.

I'm not sure I understand why changes to render() are necessary.

If you intend to use the system you've described in your own views, you can use your own variant of render that returns an appropriate HttpResponse subclass.

It's unclear to me that you can reasonably plug such a system on views provided by third-party apps, since context data generated by these views will often not be JSON-serializable.

Could you clarify the reasons for this feature request?
 
2. A template language that has a solid javascript implementation. To be able to render the templates the server side and client side must use the same language. I see two options here, either make sure the Jinja flavor we are supporting works with JS templatations (maybe https://mozilla.github.io/nunjucks/ ?), or get a handlebars implementation working with Django (https://github.com/wbond/pybars3 ?). Or maybe there's some third option I didn't think of...

To achieve this goal, I think Andrew Ingram's second suggestion is the easiest solution since Django 1.8: implement a template engine that hands over data to a Node.js process, lets it render the template, gets the HTML back, and returns it.

That's a good candidate for a pluggable application. It will probably have some specific deployment instructions ("run this Node.js next to your Django...)

--
Aymeric.

Aymeric Augustin

unread,
Jun 2, 2015, 10:00:13 AM6/2/15
to django-d...@googlegroups.com
2015-06-02 12:54 GMT+01:00 Aymeric Augustin <aymeric....@polytechnique.org>:
implement a template engine that hands over data to a Node.js process, lets it render the template, gets the HTML back, and returns it.

If someone wants to try this, https://github.com/markfinger/python-react may me a good starting point.

--
Aymeric.

Carl Meyer

unread,
Jun 2, 2015, 1:06:35 PM6/2/15
to django-d...@googlegroups.com
On 06/02/2015 05:54 AM, Aymeric Augustin wrote:
> 2015-05-30 17:52 GMT+01:00 Emil Stenström <e...@kth.se <mailto:e...@kth.se>>:
[snip]
> 2. A template language that has a solid javascript implementation.
> To be able to render the templates the server side and client side
> must use the same language. I see two options here, either make sure
> the Jinja flavor we are supporting works with JS templatations
> (maybe https://mozilla.github.io/nunjucks/ ?), or get a handlebars
> implementation working with Django (https://github.com/wbond/pybars3
> ?). Or maybe there's some third option I didn't think of...
>
>
> To achieve this goal, I think Andrew Ingram's second suggestion is the
> easiest solution since Django 1.8: implement a template engine that
> hands over data to a Node.js process, lets it render the template, gets
> the HTML back, and returns it.
>
> That's a good candidate for a pluggable application. It will probably
> have some specific deployment instructions ("run this Node.js next to
> your Django...)

I'm sure this Node-daemon technique is workable (I know of at least one
project already using it), but I'm not sure it's the easiest option.
Using Jinja2 on the server and nunjucks on the client with shared
templates works very well, is quite easy, and doesn't require Node.js on
the server. I've been using this combination for a year and a half on
several different projects and am quite happy with it.

The primary discipline involved is ensuring that you only pass
JSON-serializable objects to your templates (this is required for any
template-sharing technique). In practice, this just means you need a
full set of serializers and to use them consistently. The same
serializers should be used in your server-side views and in your API, so
the objects passed to your templates on the server and client side are
the same. (I think using serializers and avoiding passing model
instances/querysets directly into template contexts is a good practice
anyway.)

The main problem is that if you want to extend the template language
with custom tags/filters, you have to write these extensions twice. We
tend to accommodate this by just writing many fewer template language
extensions, doing more display-preparation work in serializers instead,
and relying on Jinja's built-in expressive power (e.g. macros) instead
of template language extensions.

Carl

signature.asc

Emil Stenström

unread,
Jun 2, 2015, 1:17:25 PM6/2/15
to django-d...@googlegroups.com
On Tuesday, 2 June 2015 12:58:22 UTC+2, Andrew Ingram wrote:
Based on my own experiences building isomorphic JavaScript apps with Django, I haven't had any issues where I felt Django was getting in my way. I've tackled the problem in two different ways, both of which worked without any great difficulty:

I think the reason that Django has not been in the way here is that you have pushed it out of the way, and just reimplemented features that Django should have in javascript instead. I'm not saying this is bad for your project, it's just a shame that it's not something that Django can handle. More concretely:

1. The primary web-app is actually a node process. Everything you'd consider a view is handled by React and React-Router, data requirements are handled by making an HTTP request to Django REST Framework. This isn't especially efficient, since the base response times aren't great when you're depending on HTTP for data (this can obviously be improved by getting the data via other channels).

Here you use routing and views from React instead of using Django. The problem with this is as you say latency since getting data is a HTTP request, but you also get slow initial load times since there's no content to show. Rendering on the server first to get a quick initial load, and then delegating to React would give us the best of both worlds.
 
2. Use node purely as a rendering engine. I actually created an alternative to TemplateResponseMixin that communicated with a node process via zerorpc (chosen for the sake of simplicity) rather than rendering a template from disk. This abstraction could be made neater by packaging it up as a custom template engine that takes care of converting the data into a format that zerorpc accepts (ie no querysets or rich objects). This approach was significantly faster with base response times of about 8ms for a simple page with no data dependencies, but I felt I was implementing my routing logic twice (once in Django, once in node).

This sounds excellent. It's almost like implementing a new template engine, but instead just running it through node. The downside is that it requires you to install, deploy and maintain a separate javascript environment. What js template language were you using?

I think implementing that same language for Django would give you the best of two worlds. All server-side rendering would remain like for any server-side app, but you could also send your templates to the view for fast client side rendering.
 
The second approach is easier to get running at acceptable performance levels quickly, but is quite clunky to implement. Going forward I'm probably going to favour the first approach, which essentially means turning my Django projects into nothing more than a data layer, which is fine for me because I've found that once you're using React (or similar) heavily, you're not using much else from Django anyway.
 
Did you read the Twitter post on this (https://blog.twitter.com/2012/improving-performance-on-twittercom)? They went the same route, moving all template logic to the client, found it was too slow on initial load, and then favoured the dual approach I'm hoping for.

Emil Stenström

unread,
Jun 2, 2015, 1:27:19 PM6/2/15
to django-d...@googlegroups.com
On Tuesday, 2 June 2015 13:54:44 UTC+2, Aymeric Augustin wrote:
2015-05-30 17:52 GMT+01:00 Emil Stenström <e...@kth.se>:
But what needs to happen is that the same templates that Django uses needs to be accessible to, and executable in, javascript.

For this to work, two things needs to be built:

1. A way to access the template and the template context for a view separately, so that it can be delivered to the client. Maybe the easiest would be to modify django.shortcuts.render so it returns both the input and the result. The client could then get access to this via a <script> tag when the page renders.

I'm not sure I understand why changes to render() are necessary.

If you intend to use the system you've described in your own views, you can use your own variant of render that returns an appropriate HttpResponse subclass.

Yes, I could. I was thinking in terms of what the README for my third party template backend should be. Either it's just "pip install <package>" and add this to your TEMPLATES setting", or having to tell people to also replace all calls to "render" with my custom version.
 
It's unclear to me that you can reasonably plug such a system on views provided by third-party apps, since context data generated by these views will often not be JSON-serializable.

That's a problem that I hadn't thought about. I was hoping that querysets where always serializable, but I didn't think of datetime and decimal (and others), which people might send to their views. Are there other pitfalls here? Maybe this could just be else that people would have to change to use the new template backend.
 
Could you clarify the reasons for this feature request?
 
2. A template language that has a solid javascript implementation. To be able to render the templates the server side and client side must use the same language. I see two options here, either make sure the Jinja flavor we are supporting works with JS templatations (maybe https://mozilla.github.io/nunjucks/ ?), or get a handlebars implementation working with Django (https://github.com/wbond/pybars3 ?). Or maybe there's some third option I didn't think of...

To achieve this goal, I think Andrew Ingram's second suggestion is the easiest solution since Django 1.8: implement a template engine that hands over data to a Node.js process, lets it render the template, gets the HTML back, and returns it.

That's a good candidate for a pluggable application. It will probably have some specific deployment instructions ("run this Node.js next to your Django...)

Yep, that's probably the one that's easiest to use right now. I replied to that post separately. Would be great if this could work without having to maintain a separate node.js process though...

Andrew Ingram

unread,
Jun 2, 2015, 1:40:11 PM6/2/15
to django-d...@googlegroups.com
I feel we're getting a bit derailed here, but it seems I didn't explain what I was doing well enough.

I'm saying that in both approaches I've used, I was authoring an isomorphic React application - i.e. returning a fully-rendered html document with all the React checksum IDs pre-populated. In my first approach I had a very thin express server in node that passed request parameters to React Router, which ultimately returned a rendered page. In my second approach, I had Django hand over template context and request parameters to the node process, which also returned the fully-rendered document. I picked the second approach because it performed better to start with, but I prefer the first approach in terms of how clean the stack is.

I'm a big fan of all the front-end work Facebook is putting out, I'm a total convert, it's basically solved almost everything that's annoyed me about web dev for the last 10 years. But Django has an excellent data layer, and I want to keep using it if I can.

Andy



--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Bobby Mozumder

unread,
Jun 2, 2015, 1:43:02 PM6/2/15
to django-d...@googlegroups.com
Yes, my expectation is that Django still only serves API endpoints, with something like Node.js & Ember Fastboot serving pages.

Ember's Fastboot would make for an ideal candidate as a starting point for a new design for Django’s template view layer.

-bobby

Emil Stenström

unread,
Jun 2, 2015, 1:53:03 PM6/2/15
to django-d...@googlegroups.com


On Tuesday, 2 June 2015 19:06:35 UTC+2, Carl Meyer wrote:
On 06/02/2015 05:54 AM, Aymeric Augustin wrote:
 
Using Jinja2 on the server and nunjucks on the client with shared
templates works very well, is quite easy, and doesn't require Node.js on
the server. I've been using this combination for a year and a half on
several different projects and am quite happy with it.
 
I'm very happy to hear that this works in production. I picked nunjucks as an example just based on reading about it, but now I know that this is something that actually works. Thanks for that!

The primary discipline involved is ensuring that you only pass
JSON-serializable objects to your templates (this is required for any
template-sharing technique). In practice, this just means you need a
full set of serializers and to use them consistently. The same
serializers should be used in your server-side views and in your API, so
the objects passed to your templates on the server and client side are
the same. (I think using serializers and avoiding passing model
instances/querysets directly into template contexts is a good practice
anyway.)
 
I would love to see some code here if you have it available? How do you pass the JSON and the templates from the view to the client side?

The main problem is that if you want to extend the template language
with custom tags/filters, you have to write these extensions twice. We
tend to accommodate this by just writing many fewer template language
extensions, doing more display-preparation work in serializers instead,
and relying on Jinja's built-in expressive power (e.g. macros) instead
of template language extensions.

Yeah, I figured as much. I think that Django is in a better position here than other frameworks since actual python code in templates are discouraged.

Carl Meyer

unread,
Jun 2, 2015, 2:31:41 PM6/2/15
to django-d...@googlegroups.com
Hi Emil,

On 06/02/2015 11:53 AM, Emil Stenström wrote:
> On Tuesday, 2 June 2015 19:06:35 UTC+2, Carl Meyer wrote:
> Using Jinja2 on the server and nunjucks on the client with shared
> templates works very well, is quite easy, and doesn't require
> Node.js on
> the server. I've been using this combination for a year and a half on
> several different projects and am quite happy with it.
>
> I'm very happy to hear that this works in production. I picked nunjucks
> as an example just based on reading about it, but now I know that this
> is something that actually works. Thanks for that!
>
> The primary discipline involved is ensuring that you only pass
> JSON-serializable objects to your templates (this is required for any
> template-sharing technique). In practice, this just means you need a
> full set of serializers and to use them consistently. The same
> serializers should be used in your server-side views and in your
> API, so
> the objects passed to your templates on the server and client side are
> the same. (I think using serializers and avoiding passing model
> instances/querysets directly into template contexts is a good practice
> anyway.)
>
> I would love to see some code here if you have it available? How do you
> pass the JSON and the templates from the view to the client side?

I don't have code I can share at the moment, but I'm hoping to blog
about this technique, and I'll let you know if/when I do.

It's not very complicated, though. Data gets from server to client in
one of two ways: either it is rendered to a template on the server side
from a normal Django view and then returned as an HTML HTTP response, or
it is queried by the client using XMLHttpRequest from a JSON API and
then rendered in (possibly the same) template on the client side. The
key is that you use the same serializers in both cases. So in one
project, we're using Django Rest Framework and DRF serializers. A simple
view might look like this:

def person_detail(request, person_id):
person = get_object_or_404(Person, pk=person_id)
context = {
'person': PersonSerializer(person).data,
}
return render(request, 'person_detail.html.j2', context)

And then there's also a set of DRF API endpoints for Person, using the
same PersonSerializer, so JS can query that API via XHR, and render the
very same 'person_detail.html.j2' template (or more likely, a partial
included in that template) on the client side.

Or sometimes JSON (in the same format, from the same serializers) gets
pushed to the client via Websockets (or SSE I suppose, though I haven't
used SSE myself) for real-time updates, where it again can be used to
render the same templates.

The templates get to the client via nunjucks precompilation. Nunjucks
can compile Jinja2 templates to a file of plain JS code, and then that
file is referenced like any other static JS asset. (Precompilation is a
build step, it doesn't happen at runtime.) What you end up with is a JS
object mapping template paths to functions that you call with a context
and get back HTML (and it's quite fast, since there's no template
parsing at runtime).

It's also possible to do things like smuggle JSON data in a <script
type="application/json"> tag along with an HTML skeleton and then render
that JSON on the client. Whether that performs better or worse than
simply rendering the template on on the server is a highly complex and
nuanced question, depending on factors like the size of the JSON data vs
the size of the rendered data, whether you care more about time to first
byte or time to last paint, what level of no-JS support you want, etc.
(See e.g.
http://www.onebigfluke.com/2015/01/experimentally-verified-why-client-side.html
for some exploration of this.)

Carl
signature.asc

Emil Stenström

unread,
Jun 2, 2015, 3:05:04 PM6/2/15
to django-d...@googlegroups.com
On Tuesday, 2 June 2015 20:31:41 UTC+2, Carl Meyer wrote:
On 06/02/2015 11:53 AM, Emil Stenström wrote:
> I would love to see some code here if you have it available? How do you
> pass the JSON and the templates from the view to the client side?

I don't have code I can share at the moment, but I'm hoping to blog
about this technique, and I'll let you know if/when I do.

Please do, and thanks again for sharing how you get this going. I subscribed to the oddbird blog, so if you post it there you don't have to tell me :)

Lee Trout

unread,
Jun 2, 2015, 4:02:29 PM6/2/15
to django-d...@googlegroups.com
I don't want to add any noise here- but I just had a chance to glance over this conversation and I've basically been doing what Carl describes with Angular. (In fact I joke often about calling it Djangular). I have a view that prerenders angular templates (accepts the path to the template in the url e.g. /views/renderng/path/to/ng/template.html) filling in data that is available on the server and minimizing what I'm tracking in the scope in Angular.

It's been working really well but I did have to change my Angular interpolation provider start and end markers to [[ and ]]. So I have templates similar to:

<div>Hello {{ user.first_name }}</div>
<div>You have [[ data.points ]] points</div>

Or to share a URL for a specific controller:

<div ng-controller="AuthController"
        ng-init="init('{% url "auth-login" %}')">


I also do as Carl suggested and smuggle data in script tags. This has been useful when rendering the base template and sharing global URLs.

<script>
window.foo = {
    urls: {
        dataEndpointFoo: "{% url  "endpoint-foo" %}"
    }
};
</script>


--
You received this message because you are subscribed to the Google Groups "Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.

Aymeric Augustin

unread,
Jun 3, 2015, 8:05:20 AM6/3/15
to django-d...@googlegroups.com
2015-06-02 18:27 GMT+01:00 Emil Stenström <e...@kth.se>:
That's a problem that I hadn't thought about. I was hoping that querysets where always serializable, but I didn't think of datetime and decimal (and others), which people might send to their views. Are there other pitfalls here? Maybe this could just be else that people would have to change to use the new template backend.

No matter what you do, if you're doing isomorphic rendering on the server and client side, you need to serialize all context data to the common denominator, that is, JSON.

On the Python side, this doesn't have to be encoded as JSON; you can keep an equivalent representation with Python dicts, lists, strings and numbers. However you can't keep objects and call their methods in templates, like Django on Jinja2 templates often do.

This is not a bad thing. There are advantages to pre-computing explicitly all data you need in before you start rendering the template. All I'm saying is that we must be aware of this new constraint because we aren't used to it until now.

--
Aymeric.
Reply all
Reply to author
Forward
0 new messages