Example Ember.js + Django + Django Rest Framework single-page application
How a single page applications written in ember look and work and how they integrate with Django.
In this article I'll show you an example Django + Django Rest Framework + Ember.js application. Classical posts and categories done in Ember and in Django for comparison.
Ember.js + DRF + Django Application
If you are new to Ember.js you should read my previous article which describes the basics (and shows an ember application with Tastypie backend). In this article I'll go staright to the application code.
So now I'll go through a simple application that has posts assiged to a category. Whole django project with the ember code can be found on github. Grab it, run it, modify it as you see fit. There are two applications:
blog with the classical Django implementation and
eblog with ember frontend, DRF API backend and Django.
Single page applications need a single page so in Django we just need a basic view that renders a given template:
That blog.html template has some vendor and our JS files hooked:
We have jquery, handlebars templates library, ember and django rest framework adapter for ember as well as our application files. Ember applications divide to parts so I places each in separate file:
- app.js: App initialization code.
- helpers.js: sometimes we may need template helpers.
- router.js: URL routing here.
- routes.js: page business logic here.
- controllers.js: custom controllers for pages.
- models.js: models for data available over the REST API.
- views.js: custom views for use in templates.
Let us start with standard app initialization in app.js (can have more options and code):
Blog is the
application name used in every other file.
Now we can write models for our Django models which have posts and categories:
ApplicationAdapter is the DRF adapter configuration for ember. Usually every REST generation backend needs an adapter for ember so that all features are supported (like filtering, relation handling, batch fetching etc.). In case of DRF we have ember-django-adapter.
Blog.Category and Blog.Post are ember models that match Django models exposed via the REST API by DRF. DS.attr can take one argument stating field type. It's optional, but for non string or not null fields it's required for correct handling.
We have models that will call the DRF API, which is made by serializers and two views: CategorySetView and PostSetView.
So now we can start with base ember template. In the blog.html template there are JS inserts of handlebars templates used by ember.
body.handlebars is the base template used by ember and the other templates are for our pages:
Template name passed in
data-template-name is used to populate names fo controllers, views, route and so on. For
posts we will get PostsController, PostsView etc. A page within ember application doesn't have to have custom views or controllers as ember will use built in defaults if nothing custom is provided.
So let us look at the body.handlebars:
Handlebars use similar syntax to Django templates so we have to use
verbatim tag to prevent Django from parsing handlebars templates. The
link-to tag is used to make links to pages within ember. The
outlet tag defines a place in which child templates will be inserted - so in our case all other three. There is also category list iteration and linking every category to a page (that should show posts only from that category).
each iterator has an extra
itemController which allows us to assign a custom controller for every iterated element. From our controller we have a
isActive property which will set CSS class to
current if true (STATEMENT:ON_TRUE:ON_FALSE).
Now let check our router - URLs within our application:
We have three pages linked/defied with
this.route. In case of
categoryPosts which uses a category ID we pass that as a dynamic part of the URL, where
id is our variable name. Similar for
post. Ember also supports nesting pages and some other implementation of this example could have
post as a child of
There is also
Blog.Router.reopen snipped that changes how URLs are generated. If the browser supports push state the links will look normally like
/posts/. If not default mode will be used that is based on hash
#/posts/. You can copy/past this snippet in your app too if you want better looking urls for most browsers (except older IE).
routes we have page business logic:
IndexRoute is the rout for the root of the application and can be used to direct to one of our pages that will serve as the first page. Other routes are for our pages. In
PostsRoute we fetch all posts while for
CategoryPostsRoute we filter posts by category id.
params.id is the variable name from the URL mapping. And in the end for
PostRoute we fetch a post by given ID.
posts page should list all posts so in
posts.handlebars we iterate fetched content:
Data fetched by route will be under
content. Note that ember is asynchronous. The page gets displayed while the data is being fetched and when it's fetched it gets displayed. In this simple example it doesn't change much over Django version but in more advanced applications it will be clearly visible and ember applications will be build differently than django ones.
date tag is a custom handlebars helper that formats date with moment.js library.
So let us look at the controllers:
ApplicationController is the root controller which is visible in the base template. We fetch categories in it so that the template may list them. There is also a small hack assigning current path to a global application variable. Sometimes is needed to have that path in an IF statement but it should be avoided if possible - to avoid high number of value recalculations.
categories are marked as
property and such properties are key element of ember applications. A property will calculate its value only when it is used and when variable on which it depends change. In case of category list there is no dependencies so it will never recalculate, but as it is used in a template it will calculate once and return the result.
observes type of functions are reactions to an event. They calculate every time a variable on which their depend change. They don't return anything, they just react. Observers may recalculate a lot and in general should be avoided unless needed - like in views to handle external JS widgets or animations. Ember way of doing things is by properties and by template bindings (using things in templates which acts just like a property).
PostController I've created a property that has some dependencies - post title and category name. Will return true if there is
python somewhere within those two strings.
isActive property that is used to highlight category name on the category list. Such properties handle asynchronous behavior of such applications. They will get a promise of a category or post with no value under given field. But when the object is fetched the promis will set it values and the property will be recalculated. You don't have to handle that nor do any DOM operation, jQuery event handling etc.
In the end
I've showcased a working basic ember application that uses Django and DRF as it data provider. Some aspects of ember applications are similar to django, but the asynchronous behavior makes at some point using ember much harder (for the first time). Even so single page applications can be quite handy to use for users where they don't have to wait for page to load and so on. Ember application can work as an Facebook application (like a contest) or rich content, interactive
widget on a bigger web page and more.
So if you are interested in ember get the code from github, check ember manual, try to change the application a bit or try creating something simple on your own. A lot of help is available on stackoverlow or also some on the ember IRC channel.