My colleague Gijs Nijholt just posted his blog entry lessons learned from
building a larger app with React.js,
which is about the javascript/reactjs side of a django website we both (plus
another colleague) recently worked on.
Simplified a bit, the origin is a big pile of measurement data, imported from
csv and xml files. Just a huge list of measurements, each with a pointer to a
location, parameter, unit, named area, and so on. A relatively simple data
model.
The core purpose of the site is threefold:
Import, store and export the data. Csv/xml, basically.
Select a subset of the data.
Show the subset in a table, on a map or visualized as graphs.
The whole import, store, export is where Django shines. The model layer
with its friendly and powerful ORM works fine for this kind of relational
data. With a bit of work, the admin can be configured so that you can view and
edit the data.
Mostly "view" as the data is generally imported automatically. Which means you
discover possible errors like "why isn't this data shown" and "why is it shown
in the wrong location". With the right search configuration and filters, you
can drill down to the offending data and check what's wrong.
Import/export works well with custom django management commands, admin actions
and celery tasks.
Now on to the front-end. With the basis being "select a subset" and then
"view the subset", I advocated a simple interface with a sidebar. All
selection would happen in the sidebar, the main content area would be for
viewing. And perhaps some view-customization like sorting/filtering in a table
column or adjusting graph colors. This is the mockup I made of the table
screen:
In the sidebar you can select a period, locations/location groups and
parameters. The main area is for one big table. (Or a map or a graph
component).
To quickly get a first working demo, I initially threw together three django
views, each with a template that extended one base template. Storing the state
(=your selection) as a dict in the django session on the server side. A bit of
bootstrap css and you've got a reasonable layout. Enough, as Gijs said in his
blog entry, to sell the prototype to the customer and get the functional
design nailed down.
Expanding the system. The table? That means javascript. And in the end,
reactjs was handy to manage the table, the sorting, the data loading and so
on. And suddenly state started spreading. Who manages the state? The
front-end or the back-end? If it is half-half, how do you coordinate it?
Within a week, we switched the way the site worked. The state is now all on
the client side. Reactjs handles the pages and the state and the table and the
graph and the map. Everything on one side (whether client-side or server-side)
is handiest.
Here's the current table page for comparison with the mockup shown above:
Cooperation is simple this way. The front-end is self-contained and simply
talks to a (django-rest-framework) REST django backend. State is on the client
(local storage) and the relevant parameters (=the selection) are passed to the
server.
Django rest framework's class based views came in
handy. Almost all requests, whether for the map, the table or the graph, are
basically a filter on the same data, only rendered/serialized in a different
way. So we made one base view that grabs all the GET/POST parameters and
uses them for a big django query. All the methods of the subclassing views can
then use those query results.
A big hurray for class based views that make it easy to put functionality like
this in just one place. Less errors that way.
Some extra comments/tips:
Even with a javascript front-end, it is still handy to generate the homepage
with a Django template. That way, you can generate the URLs to the various
API calls as data attributes on a specific element. This prevents
hard-coding in the javascript code:
Likewise, with django staticfiles and staticfiles'
ManifestStaticFilesStorage, you get guaranteed unique filenames so that
you can cache your static files forever for great performance.
Lessons learned?
Splitting up the work is easy and enjoyable when there's a REST back-end and
a javascript front-end and if the state is firmly handled by the
front-end. Responsibility is clearly divided that way and adding new
functionality is often a matter of looking where to implement it. Something
that's hard in javascript is sometimes just a few lines of code in python
(where you have numpy to do the calculation for you).
Similarly, the user interface can boil down complex issues to just a single
extra parameter send to the REST API, making life easier for the python side
of things.
When you split state, things get hard. And in practice that, in my
experience, means the javascript front-end wins. It takes over the
application and the django website is "reduced" to an ORM + admin + REST
framework.
This isn't intended as a positive/negative value statement, just as an
observation. Though a javascript framework like reactjs can be used to just
manage individual page elements, often after a while everything simply works
better if the framework manages everything, including most/all of the state.