Dynamic list views with Django and htmx

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi i'm going to show you how to add dynamic search and pagination to your django list views without having to write any custom javascript we're going to do this using a small javascript library called hdmx which is really useful for adding dynamic updates to your django pages without getting bogged down in javascript i've also done another video on using htmx with django forms which you might like if you like this one as well so here's the problem that we're trying to solve when you have a typical list view in django just a list of items in order for the user to update filter the list or to load more they need to reload the page and i'll show you an example um if i want to search for a term like qui i need to submit this form and let reload the page to get the new results which isn't the smoothest experience for the user in addition if i want to do something like pagination which is you know see more results i also need to reload the page um to show you another example of pagination um here's my blog with a list of items and we've got about 12 on here i think and then to see more items i need to load the next page and this is mostly fine it's not terrible but there could be a smoother experience for the user so the alternative to having to reload the page every time we update it is something a little more dynamic like this so this is a list of you that i've built using htmx and you can see that i can paginate through the list without reloading the page and i think you can see it looks a lot smoother for the user um in addition i can search for example i might search for the name blake and i can submit that and reload uh the list without having to reload the page and this makes it a lot easier for me to sort of explore different options like looking at different stages for example without it feeling clunky and slow so i'm going to show you how to take this list view which is just use it done using vanilla django and turn it into this list view which uses htmx to become dynamic and i'll show you an example of what it'll look like at the end so we can update the page without having to hit enter it just updates as we type and in addition when we paginate uh you'll see it just loads more items down the bottom and the key thing here is that there's no custom javascript required you can do this using react or jquery or something but i wanted to show you a way to do this where you were just using django and html templates and a little bit of htmx so all the code for this tutorial is available on github at um this repository i'll have a link to this in the um video comments and there's two branches that you should know about there's the branch that's got the django project before it has any htmx added so it's just regular django just html and python and then i also have a branch after the htmx has added and that's where we'll get to at the end of the tutorial um so to get started um we're going to clone this repo and i'm just going to clone it into a folder called htmxlist so now i'm going to check out the before htmlx branch and show you how to get set up and then take you on a quick tour of the code so you understand where we're starting from so to get onto the before htmlx branch i'm going to do git check out uh what is it before htmlx and that'll get us onto the right branch so there are setup instructions in the github repo and you should be able to just follow these um assuming you have python installed so i'm just copying and pasting my own instructions in we're going to set up a virtual environment and activate it if you're using windows you'll have to use a different command for this bit i'm going to install the python requirements with pip and then go into uh some folder which we'll see later and then run some management commands so we're setting up the database with migrations this is a custom management command that i've created just to set up the test data it'll take like two minutes to run or so and i'm just going to skip this bit because it's kind of boring so i'll be back when this is done okay so i've just finished that setup test data command and now i can run the django development server and you should if you're following along be able to now find it on localhost 8000 and you should see something like this so this is the uh the starting point where we're only using django there's no javascript or htmx being used here and so you should have a working search function if you search for something like and press enter you should get only results with is or maybe do something a little more discriminatory eq there you go so the search works and it should also have pagination where if you click next page it'll take you to the top and then show you the next page of results and it should just keep doing that until the page runs out which is not a great design by the way i wouldn't usually do this it's just a starting point for the uh the application that we get at the end so uh there you go you should be all set up now next thing i'm going to do is take you on a tour through the code that we're starting with let's have a dig through the current code so that we understand where we're starting from i'm going to open up this repository um in vs code here we go just drag this over here right and i might make it a little bit bigger there we go so um yep this is a django project um we've just got some sort of random files here our virtual environment the python requirements and all of the interesting stuff is in this app folder here so this is the django project this is the root of the um this is the root application that has all the settings and stuff in it but all the interesting stuff is in the web uh application so this has all of the views and models and whatever that we actually care about so we're going to start out by looking at models so in models.pi there is a post model so this is a post this is a post and each post has a title and an image hopefully nothing too controversial yet so that's the data model we're working with and the setup test data command that we ran earlier already created all the posts so we don't really need to worry about creating any more data looking at the urls next um we only have one url that we're starting out which is the list view which is just at the sort of root url path and it points at this list view which is from views so let's go take a look at that there is a single view list view and it is um basically grabbing a bunch of posts and then passing them to the template as context and so these are the posts that are getting selected so um to go through this in a bit more detail um when we load the page we make a get request to the back end and um the get parameters which are these things here the queries during parameters are sent uh as a part of that request and so here we're extracting the query string parameters and grabbing all the posts and then if there are any search parameters like uh qui for example it is then filtering down the posts to only those that contain the search term so that is this that's the whole search bit that's it we're just uh filtering out anything that doesn't have the search term in the title um i contains just means that uh lowercase uppercase doesn't matter i can do we oops uh we and it still works so it just means ignore case then we've got this uh paginator thing um this is a a django thing and there's actually documentation on it which i'll link in the video description uh which you can read more about what this does but basically you say how many posts you want on per page which for us is 12. and so if we look here one two three four five six seven eight nine ten eleven twelve and then it says next page so it's just breaking up the big list of uh i think 64 posts into blocks of 12 where we show at max 12 posts per page and we just use that to break up the list if you haven't used the pagination class before i recommend you refer yet to the documentation just to get a better idea of what's going on here and so the last thing we have to look at is the template list.html which is pretty straightforward we've got some css in the head but the important elements are this input number one the list of posts number two and number three the load more button and i'll just quickly go through these so uh the input this thing here is sitting inside a form and so by default uh when you subm when you press enter in the input it submits this form and makes a get request to uh the current page with this query stream parameter so you know it's going to sort of add this query string parameter and reload the page so if i do sort of foo whatever that is it's going to add question search equals that because of the way i've named it and that's just the way that html forms work uh the second part this list of posts is we have this sort of container of cards and then inside for each post we add a card with the image and the title so hopefully that's not too controversial and finally we have this next page button which is just a link to the current page with the query string parameter of whatever the next page number is and whatever the current search term is so that we preserve the search term uh so i'll show you quickly if we go next page we go to page equals two this only displays if there is a next page and this is a sort of just utility thing on the pagination class where it has the has next attribute and so that means that as we go through all the pages eventually we get to the end where there is no next page and then the button disappears um and that is it uh for now now we can have a look at what our new architecture will be when we add htmx to try and make this dynamic okay so i want to take you through the uh change in architecture that we're going to apply to this project uh when we add htmx and this is so you understand what's going on once we start changing the code so i've got some diagrams here sort of showing you how the um the code is going to change at the top it's the old way using just django and down the bottom this is the new way and i'm also going to show you um how that actually looks in our network requests by looking at the chrome network tab um if you haven't done this before then you can get to here by clicking inspect and hitting network or you can also press ctrl shift and i to open up the inspect tab cool and so this i think let me just double check this is the vanilla django view and this is the uh htmx view yeah cool all right so for starters let's just look at what happens when we load the page um so pretty standard stuff you're in your web browser you load the page by going into your address bar and pressing enter and then um it hits the djangos it makes a get request to the root url hits the django server runs the list view which renders the list html template and it returns the full page of html and you can look at that if you click on the request you look at the response you can see all the html and in the um sort of the new way of doing things it's exactly the same and so i'll just show you that if we just load the page um and we look at the [Music] doc which is the html that comes through you can see that it just loads all of the html and displays it in the browser nothing too interesting going on here um so now let's look at the old way of searching or the sort of vanilla django way of searching uh in our list view we might search for qui and it makes a get request for search equals qui which we can see here and this hits our django server runs the list view renders list html and it returns the full page of results and you can see that here as well it just returns um all of the html what we're going to do differently now when we use the htmx version is when we type in to the list view it's going to make a get request to a different endpoint this search endpoint and it's going to run a different view the search view and this view is going to return just to the search results not the entire page and so that's the red box here just the search results and it's also going to return the html for the button and we'll get into the details of how that works later but the key idea is that this search view this htmx view only returns the thing that changes which is the search results and so i'll show you that now um so if i search for qui here in the new version you see it's made a request to the search endpoint and you can see what comes back is just a div with the search results in it um yep and if i search for it same thing it makes another get request to the search endpoint and just returns the search results it doesn't return all the html so all the html on the page um and so we're going to be hooking that up when we make our code changes and the last thing to look at is the next page button so same deal when we um so when we uh go to load the next page by pressing the next page button it makes a get request with the query string parameter page equals two and it hits the list view re-renders the whole page and you can see that in the results and so the whole page gets refreshed and you also bounce up to the top because it's a full page refresh cool whereas in the way we're going to do it with htmlx it's a little different when you hit the next page button it's going to make a get request to this different search endpoint which is going to return only the html that changes which are the results and the next page button is going to update as well and you can see that here where if i hit the next page button you can see what comes back are just the search results and you can see how they're being updated in place and i'll show you how to do that in a second okay let's start making some actual code changes so the first thing that i'm going to do is to take this template that renders this list view and um split it up so that we have separate chunks one uh one fragment that represents the actual search results and another fragment that represents the button um because we're going to need to render them separately in the search view so let's take a look at our app in web have a look at the templates and you can see there's just list view so i'm going to take this and break it up into chunks so first of all let's make um sort of this one this red box for the search results and i'll just make a new template called uh results i'll call it search results yep and pretty straightforward i'm just going to copy the search results so i'm going to take it out of here put it into here and then i'm going to use the include tag and i'm just gonna do the syntax properly and so this should all work exactly the same way uh the include tag is just taking this and slapping it into here so no big changes yet and we can just test that out still looks good next thing is i'm going to take this button and put it into its own load more template i'm going to call that search load more the names don't really matter um and same deal i'm just going to take this out and put it into its own template and then include it and that's it that's uh all the changes we need initially to break this up into fragments and we can quickly test that out the load more button still exists very good so the next thing i'm going to do is create this search endpoint um and i'm going to do that by creating a new view and i'm going to call it search um there you go there's a new view and i am going to add that to urls.pi like so so now it's all hooked up and it doesn't return anything right now and so what we could do is get it to work exactly the same as the list view except instead of returning the list template it just returns the search results and this works this is fine and we can test it out we can go search search uh where's it next search equals qui and we do get the results um that aren't well formatted of course because the there's no css but we are getting uh the results and just the results however um this isn't great because there's a bit of duplication and so what we can do instead is create a single function that sort of consolidates these two together and i'll show you what i mean um so this is just a copy and paste job but basically all of this code from here to here i've just moved that into a single function that um means that we're not copying the same code twice so what that means is we can do this we can go you can say that posts search equals search posts request [Music] yep and can do the same thing here as well and this means there's a little less repetition the code's a bit easier to read and we can still test this out does this work yes we're getting results back yes the page still loads and so now we've got a search view and a list view um so the next thing we need to do is hook up our html to start using this view when we search because uh you know nothing is calling this view yet it just kind of exists now we're at a point where we can start adding htmx to our templates and before we start using it we need to actually install htmx and that's pretty straightforward if you go to the htmx website there's this link here and you can find the library here and if you just open that view raw it's like all the code minified so it's not very readable but it's small and you can save it and i'm just going to save it in my downloads i guess and then drag it over into my static files there it is there and now all i need to do is load it with um the django static sort of syntax uh there's also this other like kind of little trick that just adds the csrf token which we don't need for any of this actually we don't use csrf in in this search function but just as like a thing that i do every time add hdmx i add this just quickly you can also confirm that this setup has worked by um in the network tab looking at js and reloading the page and you can see that the htmx script has been downloaded and is getting run so now i'm going to show you how to set up this form so that it makes ajax requests to [Music] dynamically sort of update this list of search results rather than reloading the page all of the attributes that i'm going to show you that we're going to add you can read more about them here in this reference section of the htmx documentation so we're going to start with hx get which i'll show you now this overrides the default form behavior of reloading the entire page and instead sends an ajax get request to this endpoint by default what you'll see is that it will just replace the form with the results so if we try to use it now and press enter to submit the form instead of reloading the page it's going to hit the search endpoint and replace the form with the search results uh which is not exactly what we want but it is a nice start and we can confirm that that's working the way we expect it is by looking at the um ajax request that was made and you can see we've just gotten the uh search results back and not all of the html so that's a good start um for starters we want it to stop replacing the form and we need to tell it to replace the search results rather than the um the element itself and we do that using the hx target attribute so this says don't replace the form replace the element with the id search results and so we're just going to wrap the search results in a div with this id and dog div and then um the only other thing we need to do is to tell it to swap out uh all of the html in those search results not the default behavior which doesn't really make sense to me so let's give that a go um if i type queen out and press enter it swaps out the actual search results rather than the form itself which is nice um cool but that's not all we want we also want it to um sort of dynamically update as we type as you can see it's not doing that now it is instead waiting for me to press enter until it updates everything but we can fix that by adding this hx trigger attribute so what this does is it says you know trigger the hx get request when the form is submit but also when any uh inner inputs change uh and this delay sort of modifier just means that um wait a third of a second uh for typing to stop basically rather than um rather than just on any keystroke and you'll see what i mean in a sec so let's reload the page and give that a go and you can now see that when i stop typing it will um sort of send the uh hx get request and update the page um cool so there you go that is the input done um now we're ready to move on to the next page button which we haven't really looked at yet all right now for the second part of our adding htmlx to this page so we've got the search working and now we want this next page button to um sort of dynamically load more results and append them onto the end right now if we click it it's just a link to the next page um and if we click it it just loads the next page which isn't the behavior that we want so um very similar to what we did before we're going to add a hx get attribute to it and this means when you click the button now rather than loading a new page instead it's going to make a get request to search and experience we can actually get rid of this anchor tag as well actually i'll leave that in for a second because i just want to show you the other parts that we need to do when it makes this get request it needs to include these two query parameters um page and search and we can include those with the hxvals attribute so this is equivalent to this um and the so let's do that for now let's get rid of this anchor tag so now when you click a button this button it'll make a get request uh of course we don't want the button to get swapped out we want to instead um update the target so we're going to add hx target and let's just see how that goes for now um it's not all everything but let's let's try that out so if we click next page it should load the next page results and um as you can see it's actually not that great and even worse uh it actually doesn't update this value so it doesn't get to the next page after that because it's still stuck on page two so we'll never see page three so we need to update that somehow and the last thing to add before we try and fix that problem is just this before end swap behavior so this means instead of updating all of these search results instead append to the results to the end so i'll just show you that so we've got our first page if we press next page it then adds the next page on to the end which is good we're getting somewhere however you'll see it actually always just adds the same page over and over now so we're never getting to the third page because this number isn't being updated and you can see that in the request that we're sending so always just requesting for page two we never get to page three and that's because this button isn't being updated so there are a lot of ways to approach this to fix this problem i've chosen one which is maybe a little complicated but it's one of many ways you could fix this problem and that is using the hx swap out of band which basically means that it defines a chunk of html that when passed in the request isn't included in the target but it is instead put somewhere else so i'll just show you visually what i mean by that because it does sound a little complicated so basically currently we are returning uh this in the search results uh this sort of block of search results and we also want to return this button as well updated with the latest page number but the difference is that we don't want to include it in this we want to instead swap out the button and that's what the out-of-band swap does so i'll show you how i do that now um so i'm going to do the same sort of deal i did before i'm going to wrap this in a div with an id so that we can identify it i'm calling it load more and i'm just saying here when this is passed through uh as a sort of htmx result to to swap it out wherever it is on the page so i'm going to add this in and then i'm going to um add it into the search results as well and i'm just going to do a little include here and you'll see that this will create a problem which we then need to solve but it'll mostly work and then we'll solve that problem so we've got our list view and you can see we've actually got two buttons now but if we press next now it is going to work correctly and load every page until it runs out um and so now we just need to solve this problem of there being two buttons and the way i solve that is um and this is just something i made up you know there's like lots of different ways you can approach this kind of problem is i just put a boolean in to say in the list view just added a bit of context saying that this is the not the search view and a bit of context saying that this is the search view so that um when we render the initial uh search results if it's the search view we include the button otherwise we don't include the button and so what that looks like now is when we first load the results because it's not the search view we don't see this button and then we just get this next page thing but when you click it the next time it's been updated and i just want to show you that so oh this is our first page and this is our button and if we look at the html they'll inspect it this way if we look at the html uh you can see the button has sort of page two set in there but when we click this the button is updated so that it now refers to page three and if we look at the way that uh that's coming through the network is um when you look at the result coming through you get the search results and at the end you get this next page button with the new page filled out and if you do it again you get the next page button with page four in there now and so this way we can always update the cert the next page button to always be pointing at the correct page and that is it and just to show you that we get to the end and there's nothing left so there you go we're done um now you can use this list view to search for things without having to sort of refresh the page every time and you've got this dynamic imagination as well and we've used a simple example here of just searching some text but you can use the exact same approach to make more complicated interfaces like this one here where we've got the pagination and we've also got sort of various filters and under the hood this is the same sort of idea this is just a form so we're using htmx to swap out some elements um using the exact same techniques that we've learned here so good luck trying to apply this in your own projects check out the htmx documentation it's pretty good they've also got some decent examples and yeah i hope you enjoy
Info
Channel: Matt Segal
Views: 13,889
Rating: undefined out of 5
Keywords:
Id: _O7iwsTvVv0
Channel Id: undefined
Length: 34min 9sec (2049 seconds)
Published: Fri Dec 17 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.