Single Page Application (SPA) Architecture
Here are the additional resources referenced in the lesson:
00:00 In the previous lesson, I showed you how to add actions to ViewSets. In the next two lessons, I’m going to run through a single-page application and how you could use the DRF to help you build one.
00:13 Single-page applications have become a popular way of building things on the web, and REST is a common protocol to be used with these SPAs. Single-page applications are split into a frontend and backend. Django can be used for traditional web or as the backend for an SPA. Your Python code on the server side includes managing any sort of business logic, and then you can use the Django ORM and database for storage. On the frontend side, there are plenty of frameworks out there to help you build SPAs. Vue, React, and Angular are some of the more popular ones.
00:51 To demonstrate how the DRF can help you write these kinds of applications, I’m going to show you a small SPA. The S is a bit of a lie—I’m actually going to build two pages.
01:01 The first will be a traditional web one that just lists a series of schedules, and then the second will be a wholly-contained timeline editor, which allows you to edit the timelines associated with those schedules.
01:13 I’m going to be using the Vue.js library to do this. Vue is a model-view JavaScript frontend that uses declarative rendering. What that means is you pass it some JSON and you apply HTML templates, and as the JSON changes, Vue automatically updates the templates.
01:33 If Vue interests you, you can find out more about it at its homepage here. In addition to Vue, I’m going to use a couple of libraries that I’ve written myself.
01:42
They’re open-source and available on PyPI, so if you find them useful, go for it. The first of the two libraries is called django-awl
. It’s kind of a catchall library for me.
01:53 It contains a bunch of tiny little pieces of code that I’ve used over and over again in different Django applications, but none of them were large enough to warrant their own package.
02:03
One feature in particular that I’m going to use is called a RankedModel
. The RankedModel
is an abstract Django model that manages ordering of objects.
02:12
If you’d like to see the source code for django-awl
, it’s available on GitHub. I’m also happy to take bug fixes and contributions. Feel free to submit a PR.
02:22
django-awl
has been tested in production code, but nothing at a huge scale yet, so I don’t promise it won’t fall over. I can’t say the same about the next one.
02:31
django-bstrap-modals
is a series of modal dialogs in Bootstrap and includes REST-specific dialog boxes. This one’s also available up on GitHub, but unlike django-awl
, this has only ever been used for prototypes.
02:46 It’s probably okay, but caveat emptor.
02:50
Here are the pieces of the Sched
(schedule) app. On the data side, there’s a Django app called timeline
with a models
file inside of it.
02:58
It declares two objects for the ORM. The first is a Timeline
object, which really is just a container for entries in a schedule. And the second is the Entry
itself.
03:11
The Entry
contains a description and a time length. There’s another Django app that’s responsible for the REST API. Inside of it, you’ll see serializers.py
, containing a serializer for each Timeline
and Entry
, a views.py
file containing a ViewSet for each Timeline
and Entry
, and then additionally, another view called change_rank()
. The Entry
object inherits from django-awl
’s RankedModel
class.
03:40
This is what specifies the order of the entry inside of the timeline. RankedModel
has logic inside of it that ensures the ranks are consistent if one of them changes. This logic is done in an overridden .save()
method.
03:55
Unfortunately, the DRF’s update methods don’t actually call an object’s .save()
, so the side effects that are done as part of RankedModel.save()
method will not get called if you make a change to the rank through the REST API. So as a result, I’ve written a additional view called change_rank()
, which specifically calls the Entry.save()
method to ensure that the rank reordering happens properly.
04:24 Here’s how things work when a user hits the application on the browser. First off, you visit the timeline page.
04:32
That hits a view on the Django side that’s inside of the timeline
app in its views
file. The view is called edit_timeline()
, and it renders a template called edit_timeline
.
04:45 That template contains HTML as well as the Vue.js components. The Vue.js components initialize, and then as part of the initialization, the view component will make a REST call to fetch the timeline that’s being edited.
05:05
That REST call goes to the API on the Django server and hits the TimelineViewSet
.
05:12
The TimelineViewSet
serializes the Timeline
, which then serializes the nested entries, and those are returned back to the browser application as JSON.
05:24 That JSON is then used by Vue.js and its internal templating mechanism to present the actual page.
05:33 Inside of the client application, there are a few things you can do. The first would be to add a new entry. When that is triggered, Vue.js calls a method on a form. That form is a Bootstrap modal form.
05:47
The method is .show_create()
. .show_create()
takes a REST API URL—in this case, the one for entries/
—and some data for the entry that is to be created. The ID of the timeline and the rank are set by default, the ID of the timeline is the parent timeline for this page, and the rank is set by default to one more than the highest ranking item on the page at the moment.
06:13
A form is displayed. When the user submits the form, the data from the form, as well as the hidden_timeline
and rank
fields, get sent to the server by .show_create()
.
06:24
This is a REST call. That hits api/v1/entries/
, creating the object on the server, which responds back with JSON of the object created and that’s populated inside of Vue.js.
06:39 A similar process happens when an entry is edited.
06:45
The same form is used, but this time it calls a method called .show_patch()
. .show_patch()
takes two parameters: the URL of the object being edited—this would be the entry object inside of Vue.js that’s actually being edited and its .url
attribute—and then the data from the actual entry object. .show_patch()
shows the same form pre-populated with the data from the entry object.
07:12
The user can then make changes. When they save the form, bsmodals
makes a REST call. That REST call is to the entries object with the given ID, sending up the fields from the form and patching them on the server.
07:27 The REST call returns the resulting changed data and Vue.js is populated with the change and the change is reflected on the screen. The other possibility is one entry is dragged around on the screen, changing its order.
07:43
This is done through a library called Vue.Draggable. Vue.Draggable has a hook on it for the onMove
event. The function that is called in the onMove
event figures out what entry has been changed and calls through REST the change_rank()
call. This passes in the ID of the entry being changed and the new rank. That calls the specialty view that ensures that the Entry.save()
method gets called, which makes sure that all the ranks are set correctly.
08:14
And in this case, because multiple ranks may change, the change_rank()
view returns all of the entries for the timeline. Vue.js then updates its copy of it based on the JSON returned and then updates the screen.
08:30 I hope this gives you a little bit of a glimpse of how these things work and how you can quickly slap together some items with very little code on the server simply by using something like the REST APIs you’ve seen in this course.
08:44 Next up, I’ll take what I just showed you in pictures and show it to you in code. Just as a little warning, it’ll be both Python and JavaScript to show both parts of the code. Be prepared.
Become a Member to join the conversation.