Here are the additional resources referenced in the lesson:
SPA Code Walk Through
In the previous lesson, I showed you an example of an SPA architecture for the sample
Sched application. In this lesson, I’m going to dig in deeply and show you some of the code that goes along with it. Even for a relatively small application, there’s a fair number of moving parts here.
If you’re going to run the sample code for yourself, you’re going to need a couple things. First off, you’ll need to install the dependencies. The dependencies are listed inside of
In order to keep your Python environment clean, it’s best practice to use a virtual environment for this. You can install all of the
requirements.txt file by using the
-r parameter to the
pip install command.
01:47 This is a Bash shell script. It will only work if you are using some sort of Unix derivative, so if you’re on a Mac, on Linux, or you’re using the Windows Linux subsystem, then you can use this command as a shortcut.
Actually, you’ll only need the last four, because if you’ve got a brand new environment, there won’t be any PYC files or a SQLite file to get rid of. The first command,
wipe_migrations, comes out of
django-awl. It destroys any migration files.
02:28 I use this command when I’m playing with a brand new project so that I don’t create overly complicated chains of migration history when I don’t really care about the migration history because I haven’t gone into production yet. So it starts me clean each time.
Let me show you what this application looks like in the browser before I show you the code. Offscreen, I’m running the Django dev server, and here I’m going to hit the homepage of
03:23 Clicking on Peru takes me into the second page of my single-page application, and this one’s a little more SPA-ish. This is built with Vue.js. Each row in the schedule corresponds to an item in the database. All of the rows are being sent down via JSON and then interpreted by Vue.js.
03:54 This popup is one of the Bootstrap modal popups coming from that library that I told you about. The popup is REST-aware. This form inherits from a base class that knows how to create things in REST.
It uses the names of the fields inside of the form and populates an AJAX call up to the server with a
POST method. By doing this, I need very little code to be able to create new objects on the server.
Clicking the Delete button brings up a confirmation dialog. These dialogs are also part of
DELETE on the object in question.
This no longer follows the movie, but you see what I’m talking about. The movement updates the rank of the item, affecting the order on the server, and this has to be done with a custom action in order to take advantage of how
RankedModel work. Here are the models for the application.
There’s two things in the database. One, which is a timeline, which is named and groups a series of entries, and the other is the entries. Lines 6 through 10 defines the
Timeline and the
Entry object starts on line 12. Let me just scroll down here for you.
views file that defines the two pages in my single-page application. Line 6 is the homepage that lists all of the timelines, and line 14 boots up the single-page application part of this single-page application.
Up until now, I’ve been showing you the core app, which is for the timelines. I’ve also created another app called the
api. I’m grouping all of the things that’ll be available through REST inside of the Django
TimelineSerializer contains the entries within it, so I’m nesting
Entry objects inside of the timeline. Here’s another use of the
source attribute. The name
entry_set might be a little confusing inside of the HTML or for anyone who isn’t familiar with Django, so I’ve changed the serializer to call the entries
This will be the
id of the foreign key of the
Timeline object. This is necessary because you can’t have an entry without it pointing at a timeline, so when you go to create a new
Entry, you’re going to need that
Inside of the
api/views file, I define the ViewSets for the DRF views that I’m going to use in the application. The first two items here are the vanilla ViewSets for the
Timeline and the
Entry. I’ve specified their corresponding serializer classes and the corresponding querysets.
Let me just scroll down. The abstract
RankedModel class has logic inside of it that ensures that the ranks stay in order. The logic of all of this is done inside of an overridden
The DRF does not call the
.save() method on an object. It directly updates the database. Any logic in an overridden
.save() is not going to get executed. As a result, I’ve defined a new view here that is specific to changing the rank.
I’m not going to use the built-in update methods of the DRF—I’m going to update it myself manually to ensure that the
Entry.save() method gets called. Except for this being a special case as to why I’m doing it, the rest of it is a view like you’ve seen before.
I’m using the
@api_view decorator to indicate that this is a
GET. This is a little hack-ish. To be more technically correct, I probably should have made this a
PUT or a
PATCH, because I am editing an entry.
This is one of those many cases where REST is kind of a suggestion rather than a rule. Using the
entry_id, I get the object. I change the
.rank to the
new_rank provided and call the overridden
10:36 I then re-query all of the entries that are associated with this timeline, and serialize them, sending them back down. This means when a move has been executed the Vue.js application can get all the data at once as to what the new ranks are of all the entries that were affected.
This is part of the
edit_timeline.html file. The
bsmodals dialogs, a utilities function that deals with CSRF prevention, the actual Vue.js application inside of
timelines.js, and a third-party library that allows me to do drag-and-drop with Vue.js.
You’ll see the use of that variable throughout this file. In lines 8 through 11, I create the data for the Vue.js object.
.has_loaded is used to indicate whether or not the screen should be rendered yet.
entries are all the JSON that come from the server—
name being from the
Timeline object, and
entries being the array of JSON that specify the entries on the display.
Django has a whole bunch of security mechanisms built-in, one of which is to prevent problems with form posts. This is done through the CSRF token. The
get_cookie() methods come out of the
utils.js file and are used to make sure that a
POST can happen.
Once everything’s loaded on the page, the last call here is to
refresh_view(). This method is what actually does the REST call. Inside of
refresh_view(), I use jQuery’s
.getJSON() method, passing in that global
bsmodals library has a way of declaring forms and using REST methods to update objects that are associated with them. In line 22, I’m using the
change_form()—which you saw, that was popped up when you hit the Add button—and I’m calling the
.show_create() method on this form.
.show_create() knows how to talk to REST using a URL, doing a
POST, creating a new object. The first parameter to this method is the URL of the
'/api/v1/entries/', because I’m creating a new entry.
15:16 The second parameter is the default data for the dialog box that’s being popped up. The dialog has two hidden fields, which are the timeline and the rank, and then the two fields that you saw—the title and the length. That populates the dialog box.
edit_entry() works in a similar fashion. Once again, using the same form, this time calling a method called
.show_patch() takes the URL of the entry that was passed down as included as part of the JSON payload, the entry object itself, which it uses to populate the form, and then when the user hits Ok in the dialog box, a
PATCH is issued to the server. The REST code is triggered, the entry is updated. The third parameter to
.show_patch() is once again a callback. Once the entry has been patched, the server responds with the updated entry.
You saw on the screen that I’m using a third-party library for Vue.js called Draggable. The Draggable library comes with its own tag, and inside of that tag, I have specified that when a drag action completes, that this method
entry_moved() gets called. You’ll recall that I don’t want to just call
PATCH here on the rank number, because I need the object’s
.save() method to get called on the server.
So I’m calling a specific, custom REST action instead. I do that by using jQuery’s
.getJSON(). The URL is the
change_rank/ URL that I showed you earlier, complete with the ID of the entry that’s being changed and its new rank. And
.getJSON() has a callback,
Similar to the Add button, each Delete button is also wired to a function. That’s this
delete_entry() method. Inside of that, I’m using another dialog box out of the
bsmodals library called
confirm shows a title and whatever text you give it and presents a Yes and No button.
The REST call happens on line 56. If you get here, then the person hit Yes. It takes the
Entry object on the client side, grabs the URL, and does a straightforward AJAX call using HTTP
DELETE on that URL. The object has now been deleted on the server side.
18:57 If you’re truly interested in how all those pieces work, you’ll probably want to dig into the sample code and walk through it yourself. Next up, the last lesson—a summary of everything you’ve learned.
Become a Member to join the conversation.