Progressive enhancement with Django and Backbone - how to integrate the two?
Asked Answered
B

3

7

I have a very simple Django app that lets users submit a form and see a filtered list of holidays, based on country, duration and price.

I would like to use Backbone in the front-end, so that users with JS enabled don't need to GET to see results, but can load them dynamically.

I want to use progressive enhancement, so that users with JS get the Backbone experience, and users without JS can still use the form. I also want to follow the DRY principle.

My question is how best to do this. Are there examples of using the two together with minimal repetition? I'm thinking in particular of:

  1. Routing a URL like /italy/1-week/from-500-to-1000/ - do I now need to write two sets of routing code, one in Django's urls.py and one in Backbone's router, to get the country/duration/price parameters?
  2. Filtering the data, based on the parameters - do I need to write two separate ways of doing this, one in views.py and one in Backbone? (I assume I can at least use a single API for both calls.)
  3. Rendering in templates - do I need to write one list template for Django and another for Backbone, or can both use the same templates?

The best (only) example I've found of integrating Backbone into Django so far is Josh Bohde's Django Backbone repo, which is not progressively enhanced.

I've also found this blog post on progressive enhancement with Backbone and Rails, but it would be really useful to see something similar for Django.

UPDATE: Just found this SO question on a similar topic - is the situation really as hopeless as the answer makes it sound?

Bruno answered 4/1, 2013 at 11:42 Comment(0)
O
3

is the situation really as hopeless as the answer makes it sound?

Pretty much. But, since I work on a site that used to be Django-based but is now becoming Backbone-based, I can offer a few thoughts:

Routing a URL like /italy/1-week/from-500-to-1000/ - do I now need to write two sets of routing code, one in Django's urls.py and one in Backbone's router, to get the country/duration/price parameters?

Yes, but there are ways to minimize duplication. The approach we took was to have Django spit out all the URLs as JS variables on to our main HTML page template:

<script>
URLS.report_error = "{% url app.log_client_error_view %}";
URLS.access_file = "{% url app.access_file_view 12345 %}";
</script>

Now we have a pattern of using 12345 for the parameters in every URL we generate; this makes it easy to convert that URL back in to a Backbone route regex, because we can basically replace 12345 with ([^/]+).

In the interest of full disclosure, we do have a bunch of route regexs that are written "by hand", but this isn't because we couldn't automate them; it's just that we're moving away from the Django side of things, so we have no reason to clean that code up. If you want to get hard-core about supporting both, you should be able to come up with a pretty easy/simple translation scheme.

Filtering the data, based on the parameters - do I need to write two separate ways of doing this, one in views.py and one in Backbone? (I assume I can at least use a single API for both calls.)

This is a basically unavoidable problem on any site, not just Backbone/Django. You have to filter data on the server-side because you can never trust the client-side (the user could disable JS, for instance). At the same time, server-side-only filtering is sooo 1990's, so you need to create (duplicate) logic for filtering on the client-side also (that way you can tell the user "you forgot to provide field X" without waiting for a round-trip to the server).

However, there are ways to limit this duplication. I didn't work on this piece myself, but I know a co-worker managed to use Django forms in an odd way (he took the form Django provided, then parsed them slightly before using them as a template for a Backbone View). This didn't eliminate duplication entirely, and unfortunately I can't remember any details, but it did help.

Rendering in templates - do I need to write one list template for Django and another for Backbone, or can both use the same templates?

Handlebars templates have a similar syntax to Django templates if all you're doing is variables ({{foo}}). If you want to share logic between, the two have slightly different syntax ({% if foo %} vs. {{#if foo}}), but they're close enough that if you don't mind doing a little parsing work you should easily be able to convert one in to another.

So yeah, you're taking on A LOT of work just to support a very small subset of your users (the ones with browsers that can't support Backbone). I strongly recommend you look at your user's browser stats on somewhere like Google Analytics (or look at general web stats if your site isn't up yet) to decide if all that trouble really is worth it for such a small percentage of your user base. Without statistics, you can't know how small that percentage is, and obviously a key factor in that decision.

For us the choice was obvious: require our users to use browsers made this century (which is pretty much all Backbone needs) and just go all Backbone. But if that choice isn't as obvious for you ... good luck trying to DRY up your Django and Backbone code :-)

Oilcloth answered 4/1, 2013 at 19:39 Comment(1)
Thanks, that's really helpful. My desire to enhance progressively is partly based on the need to provide content for search engine bots, as well as real-world non-JS users.Bruno
O
1

I just read about a totally different solution to this problem that I thought I'd share: HTML Snapshots (https://developers.google.com/webmasters/ajax-crawling/docs/html-snapshot). The page I linked is Java-based, but you could certainly set something similar up in Python/Django.

The basic idea is that you setup a headless Javascript runner on your server, and when a web crawler hits your site you use that JS runner to generate the HTML that your Backbone code would have generated had it been run normally in the client. It then sends that HTML back to the web crawler, letting you have one set of code for both client and server.

There may be some minor potential issues with running a headless JS runner (they're not 100% identical to a web browser's built-in JS) but when used for this "HTML Snapshot" approach they shouldn't be too relevant.

Oilcloth answered 6/1, 2013 at 17:9 Comment(2)
Interesting. Looks like I could maybe use webkit? #6025582Bruno
Ah, I hadn't thought of that, but it looks promising.Oilcloth
B
0

Not sure if you're still grappling with this but I have been working on finding some solutions to this problem. I wrote up a preliminary blog post on it:

JavaScript frameworks interoperability with Django

which I will be following up in a couple of weeks with a full on example of a "Notes" app done using Django + Backbone + Marionette + Some Other Plugins.

The code will demonstrate how to use Django templates at the client side as well, so it may be useful to you.

If interested you can either following my blog or twitter (better) @sid_azad

Bora answered 5/6, 2013 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.