Django + HTMX Example App: Progress bar, infinite scroll and modal with NO JavaScript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone in this video i want to show you how to build an app in django using htmx to basically accomplish a bunch of things without javascript so let me show you what those are so what this allows me to do is it allows me to search for a channel using the youtube api select a channel and then get all of the videos on that channel in a certain order and this progress bar just tells me how long it's taking to generate all of this data because i'm querying this api over and over again and now we see here on my channel i have a bunch of videos that are appearing i can click on the link and it takes me directly to the video i can see how many views it has how many likes and when it was published and we see it's in order of the number of views i also have an infinite scroll down here so as i uh go down it loads more results so it loads 50 at a time and then once it gets down to the bottom it stops the infinite scroll and then also i can just delete one of these videos and it will remove it from the list of channels that i have so i'm doing this without using javascript at all so that's the point of this video i want to show you how to do this without javascript even though we have all these things that are dynamic on the front end this is basically a single page app but there's no javascript at all because i'm using htmx in addition to hdmx i'm using celery so that's how the progress bar is done and with celery i have rabbit mq and redis to make sure celery can keep track of the task and the status of the tasks as it happens so i can generate the progress bar i'm using the youtube api so i can get this information and then i'm using django to build the rest of it so let me just demonstrate with um different channels so another channel would be something like it would be tech let's just search for text see what appears um so let's try this vertex it's in a different language but i'll try that and then we'll say python which channels have python in their name so let's try python here and then we generate the results and we see the progress bar and then we see the results here we see the channel names and all the information with the infinite scroll so this is what i'm going to focus on in this video if you want to learn how to build something like this follow along i have the code in the description below so you can get it i'll have the starting point for the code and then the actual finished product that i make in this video so if you want to follow along just take the starting code because i do have some things already and then the finished code will have everything that i create in this video so let's get into making this app okay so to begin let me show you what i have already first i have a virtual environment running and i've installed a few things i have django celery redis a library called celery progress and i also have requests so i can send a request to the youtube api next i have this docker compose file i have two services redis and mq the reason why i have those two is so celery will work well so rabbit mq just hosts the list of incoming tasks from celery and red stores the results of the salary tasks so i know how much of the task has been done and if it's complete or not next i've already created a django project called video tracker in here i have the celery stuff already so i'm not going to explain how all this works but you just need a file like this so you can use celery and then also in your dunder init you need a little bit of code to get celery to work but basically this just integrates celery into django because there's not a celery extension that i'm aware of for django but it's just this code that you paste in here is 24 line so it's not that difficult here in the settings.pi i have created an app already called app so that's where i'll put all my code and then i also have the configuration for the redis back-end and the celery broker so celery broker here is just the url for the rabbit mq service and then i have redis down here for the celery back in and then underneath this i have the youtube api key i'm not going to show you it because i don't want you to use my api key there are limits on it so i can't show you like i would for other api keys so that's about it for the things that i have already so what i want to do is i want to get started on this so the first thing i think i should do is create the models so i can save the data that i'll be retrieving from the api so there are a couple things that i want to retrieve i want to retrieve the channel data so like the channel id the name of the channel and any other relevant information and i also want to store the video data so the name of the video the number of views the number of likes the date that it was published and so on so i'll create a couple of models for this and i'll put them in my models.pi file so the first model that i'm going to create i'll call this channel so channel and it's going to import from models.model and then i want the channel name i want the playlist id so what the playlist id is is for youtube every channel has a playlist id and that playlist has a list of all the videos for that channel so that's why i need the playlist id so it's not an actual playlist it's just a list of all the videos that a particular channel has released i'll put the thumbnail url here and then finally i'll put the description i think this is all i need so for description i'll make this a text field for thumbnail playlist and name i'll make them char fields so i'll just give them a max length of let's say 200 i don't know exactly how long these things will be but 200 is fine so now i have the channel model the other model that i want to create is the video model so this will hold the data for the videos so this inherits from models.model again and i need the title of the video so this will be a char field let's just say 200 and i'll just copy that for the other ones that needed i need the number of views so this will be an integer field so integer field for this let's say likes integer field and let's see i can create links to the videos so i'll have the youtube id so char field and then we have max length is 200 again so this will allow me to create links to the actual video on youtube and finally i want let's say the date published so this will be a date field and that's it and then i need to create a connection between these two so every video belongs to a particular channel so i can just create a foreign key field foreign key and it's going to be channel and then on delete we'll say models.cascade okay so let's go ahead and migrate these so i should need to migrate the default models first so let me go into here and python managed to apply migrate so this should give me the default models which it does and then i'll make migrations and it creates them and then i just migrated them so now these two tables are in my database so now that i have the models out of the way let me work on the views so the first thing i want to do is i want to bring up the main page so it's going to generate some html and then i'll be injecting everything into this html so i'll go to views and before i forget let me just go ahead and import those two models so from dot models import channel and video and then the next thing i want to do is i want to create this index view and i'm going to return render and i have this template here that i've created already called index so let me just open it and show you what it is so it's just going to be a plain template i'm using bulma for the styling so i have that style sheet imported and i have the title youtube video tracker so let me go ahead and return this so request and then the name of the template is index.html it's going to take some context so let me just go ahead and create that and i'll fill in the context as i need it so let me create a url for this so i'll go ahead and create a new file called urls.pi in my app so urls.pi and i'll just take the urls.pi from my project and paste it in here and just make the modifications that i need so i can get rid of admin i need path and for the path it's just going to be empty because it's the index and then let me import views so from dots so this particular package import views and then i want use dot index okay so now i can go to my other urls.pi and then i can import include here and then create another path it's going to be empty as well because like i said it's just going to be on the index and then i want to include the name of the app so app.urls just like that so now i should be able to run this and see my blank page just to confirm that everything is running so the server starts i'll go over here and i see a blank page it says youtube video tracker at the top so this is exactly what i want it's running so now let's start working on some functionality so the first thing i want to do is i want to give the user the ability to add channels to the database and what i can do is let me close out these urls.pi file the first thing i'll think about is i want to be able to show them all the channels that they've added so far and then if they want to add more i can show them so i'll kind of do both of those things at the same time so let me start by just querying my channel model and getting all of the channels that i have in the database of course i don't have any yet but this is something that i'll need to do so channel.objects.all and what i'll do is i'll just put this in the context so we'll say channels channels now i can go into the template and i can think about where i'm going to put this channel information now because i'm using htmx one pattern that you'll probably see many people use is the idea of having like partials of html that will be used by htmx and by your templates directly and the reason why you're doing this is because the whole point of htmx is to return basically partial html so it can inject it in certain parts of the page so by having partials in your templates directory it makes it a lot easier to do so what i'm going to do is i'm going to create a new directory in my templates directory called partials so partials and then in here i'll create the partials that i need i think i only need a couple for this video but if i need more i can simply add them so inside of personals i'll create i'll create a new file i'll call this channels.html of course you can call this anything you want but i'll call it channels.html and inside of my index i'll just include it so to include something you just type include and then the name of the particular file that you want to include so channels.html and before this i need just need to put partials and close out the bracket like that and in here i'll just try to put h1 and we see that channel appears so that works we're able to include it just fine and inside of channels i want a couple of things so first i want the search bar that will allow me to search for new channels and then i also want the section that shows the list of channels so i'll do both here so i've written a lot of the html already so i won't spend a lot of time in this video writing html so just know that but i'll explain everything that is being copy and pasted in here so you understand so first i don't need to copy and paste anything i'll just create a div and i'll give this an id of let's say channel dash section right so this section will have everything that i need for the channel stuff so the search and the actual list of channels and the reason why i'm putting this in a div is because htmx will eventually target as div and replace the content within it so every time the channel's list changes i'll be able to replace this with the new channel section and the the updated channels will be reflected on the screen as we'll see momentarily and now i want to bring in a section for the search bar and basically if i just save that and refresh the page we see i have this search i'm going to type in something into the search press the button and it will search for me so right now it's a form and it has the default form functionality but in a moment i'll change this to use hdmx so it works like we want it to work so in addition to this section what i want is a place to put the modal so i can see the results so what i'll do here is i'll just say div id and then we can call this like search results and what's going to happen later is i'm going to inject the modal into this and the modal will be active as i inject it so when htm x replaces this section with the modal that is active we will see the modal on the screen and of course we'll get to that momentarily and then finally i want a section for the actual channel results so it's going to be a loop over all the channels that i've queried from the database and i'll just display the information for those channels so this is what the code looks like for that section for the channels so i'm going to loop over this so what i'll do here is i'll start with an if statement i'll say if channels so meaning i have a list of channels that has at least one element in it and i'll have an indiff down here and then what i'll do is i'll have an else and i'll simply say something like um no channels found and this should work right away because it should say no channels found so no channel is found right there which is what i want and let's see if i can make this a container just to kind of put it in the right spots and looking at this i think it should go in here so we have the div container yeah so the if statement should go after the article so let me just replace that and put the if statements above the article so here the article in this case is just the list of channels okay so i think i can take the container out of here and try that again okay so yeah no channels found that's exactly what i want and then in here it's just going to be a list so if i go over to views and instead of channels i just pass in a list of a couple of things we see one so now i just need to put the loop so four channel in channels and the n4 goes after this article now i should see two of them which i do and i'll replace them with the actual information that i want so this should be channel dot description and this will be channel dot i think channel dot name yeah and then i need the thumbnail url so i'll go back to channels here and then the source for the image right here will be the thumbnail url okay so i'll go back here and remove this and replace with channels so of course we don't have any data in the database yet so we'll say no channels found but once we have some channels we'll be able to see them here so let's work on that now so how do i get these channels well what i want to do is i want to search for a channel so let's save my channel i want to click this button and i want a modal to appear with the results of the search so a couple of things need to happen first let me create the code for the search results so call this channel [Music] search results.html so here's where i'll have the code for the search results and let me just go ahead and bring that in now so you can kind of see okay so this is what it looks like so it's very similar to what i just created for the list of channels on the main page the biggest difference of course is this will be a modal so it has the modal classes here from bulma and it is active so whenever i inject this into the page it's going to appear immediately and i have the if statement in the loops already and basically it's the same as in channels i'll get the information from the results from the api and i'll just put them in here so let's work on that now so to think about what needs to be done is we need to create a view that will search the youtube api and then return the results as html so htmx can take that html and generate the modal so in this case the html is this code that i just added here but of course it's going to be dynamic depending on the channels that i actually search for so first i can create a view called channel search and of course it takes a request and i'll start the render down here and it's going to return that partial so partials slash channel search results.html and it's going to have some contacts here and this context will eventually hold the api results so the first thing i need to do is i need to figure out which channel did the person search for and the way i can do that is by looking here on the index or actually channels here on the search bar and we see for the search we have the name q so htmx will eventually use that name as a key to specify what channel the person searched for so i'll just use this q and in django what i can do is i can say requests dot gets capital get and then i can look for the key q and i'll just call this query and i'll pass this to the api so i can perform that query next i'll construct the url for the requests so the url is going to be equal to an f string and let me just bring in some of the api url so this is basically how they all start google apis dot com slash youtube version three and then the particular part of the api that i want to use so in this case search and here for q which is the query i can pass in query just like that and then i need some additional information so what i want to search for is i want to search for channels only so with this search you can search for videos so i want to search for channels so i can say type is channel and then i need to specify the type of data that i want for the channel so in this case it's called a snippet so part is snippet so this part is just required for the youtube api and then finally what i want is the api key so key equals uh youtube api key and actually i'll bring this in from my settings so what i can do up here is i can say from django conf configuration import settings and then i can do settings that youtube api key and that's just in my settings app pi that i didn't show you but this is the actual api key that i'll be using next i want to perform the request so i need to import the request library so import requests and what i'll do is we'll say response is equal to requests dot get so this is a get request we're passing in the url the url has all the information that we need and then i'm going to get a response and i want to convert that to json so response dot json and i want to get the items out of it so the items are just the list of things that gets returned from the api let me show you the api so if i go to search here and list just kind of shows you what it looks like if i can find it here okay so we have the response so it has these tokens and the tokens for the pages we won't use that here but we'll use that for something else but the items here are the things that we want so the items will return the search results and this is what the search results will look like we see the snippet here and then we see some information like the channel id the title description and so on so this is the information that we want to get eventually so if we go back here and we call this something like results we can pass this to the context so results results just like this so let me just put a space here and then if we go to the search results here we see we're checking for the results and then we're looping over them so i have three things that i want to appear i want to have the thumbnail i want to have the title and i want to have the description so let me go ahead and add those so for the thumbnail we start with results so we have an instance of result here and to get it we have to go to snippets.thumbnails.default.url and this is just coming from the results so snippets thumbnails and then they have a default as well so you just don't see that but there is a default and then url there are other types of thumbnails as well like different sizes but we're using default and then for the title we'll do something similar so result dot snippet dot title and then for the description result dot snippet dot description all right so i have those things and that should be enough to display the search results so it won't actually allow me to add it yet but it should be enough to display so this is where i bring in hdmx to get this to work correctly so the first thing i need to do to make hdmx work is i need to bring in the script so the one thing that we need javascript for is the actual htmx library so let me go over to index.html and i'll just put this before the closing body tag so this is what it looks like this is just from the website you can go to any cdn that hosts javascript libraries and get this and next i need to think about the events that i want to trigger this so if i go over to channels where i have my search i want something to happen when they click on this search button and because this is a form i'm going to use this htx git so i have the the htx git here and what i want to do is i want to send a git request to a particular endpoint so here i have my channel search and i want to send the request to the endpoint for this of course i haven't created the url for this yet so let me go ahead and create that so path and we'll call this channel search actually just one word i like channel search and then views dot channel search and then we'll just name this channel underscore search channel dash search okay so we have this channel search and then inside of my channels here for the htx gets so i'm performing a git request whenever the event for this form is triggered so url and then the url will be channel dash search doesn't require any additional arguments here just the channel search url and then i need to think about where i want this to go when i get the results so this is going to send a request to channel search which is here and this is going to return this partial here which is a modal so i want to take this modal and i want to inject it into the search results right here so i can use hx target is equal to search results just like that with a hash representing the id and i believe that's going to be it for searching and seeing the results so let's give it a try so let's go over here refresh and i'll search for my channel so let me just remove the query from here because that was from when the form was having the default behavior so i'll search for pretty print it hit search and now i see the search results show up and we see pretty printed is the first one someone has a channel called pretty print as well that's not mine and then the other results are just random things and i can't close the modal yet i'll get to that later but i can search for another channel let's say google i believe google has some official channels yes so google google students life at google and google taiwan and i'm only getting five results here but that's really all i need if you search for the channel name directly you're probably going to get it in the first result or the second result so we see that the search results are working and like i said this is pretty straightforward what html is doing is it's sending a request to this channel search url it's passing the q which is the value of the input and then it's taking that queue in the view sending a request to the api for youtube that searches it's getting the results and passing them back to the search results template here this is a modal that's active so as soon as i inject this html into the page it's going to display an active modal if i didn't have this is active then you wouldn't see anything normally modals need to be activated and deactivated through javascript so this is just the trick that you use in htmx when you want to display modal you inject html that has a modal that is active already and then it will just display on the screen so now that i'm able to search for these channels what i want to do is i want to be able to add them to the database so what i'll do is i'll allow the user to add something to the database by just clicking on the name of a channel and that will perform a request that will add the channel to the database let's go ahead and create the view for this first so we'll call this add channel and it's going to take in requests like always and what i want to do with this is i want to get the channel id so i have the search results from the api in the modal but once i perform a request like all that information is lost i mean i could pass all the relevant information but what i'll do is i'll just use the api again so i'm going to pass the channel id from the front end to the back end and then i'll query the api using that channel id again and then i'll save the information about that channel in the database using the information from the api call so first i'll start with the url or the api call so this is going to be an f string and i'll just bring in the first part of the url okay so this time i'm hitting the channels in point and then we need the id for the channel so this will be the channel id and for this i'll pass it in a different way i'll put the channel id here and this will just go directly into the url which we'll see in a moment next for the data that i want to return for part i want snippet again and i also want content details and then finally i need the key so i can just copy this and paste it on the end because it's exactly the same so this will create the url for the request you get the information for a particular channel id and what i'll do is i'll send that request so it's going to be similar to this before so i'll just copy and paste this so send a request to this url i get the response and then i can say results is going to be equal to response.json i want the items and for this you can search for as many channels as you like well up to 50 so it will return a list of items but since i'm only searching for one i just want to get the first item in the list so i'll just get that index directly and i'll put it in result here now i can bring in the channel model and i want to create a channel based off this so lowercase channel will be the instance of the channel model and i want to add the data that i need so i believe name is what i named it for channel and then title is for video so name for the channel is going to be results results and then we have these snippets and the title so this is just like before the only difference is the syntax here i need the square brackets in a template i need the dot notation just because of how the templates work so now i need the playlist id and the playlist id comes from the content details so content details and then inside of content details we have something called related playlists and inside of related playlists we have uploads so this will give me the playlist that represents all of the videos that a channel has next i want the thumbnail url and this will be results snippets thumbnails this should be default and then url just like we have in the templates we can get the description snippets uh description and i believe that's it for the fields for the channel yeah so i'll create all of these and i'll do channel dot save and then what i want to do is i want to return some html right so htmx is going to send a request to this view using the channel id i'm going to query the api to get the data for the channel and i'm going to use that to create a role in my database a channel object i'll save it and then what i want to do is i want to return some html so of course with htmx you always return html so what i want to return in this case i want to return this the channel section so this is what it looks like when it's in the template and basically it's going to have the search bar like we saw we're going to it's going to have this empty search results and it's going to have the actual channels so what does this mean i'm basically taking the existing channel section and i'm replacing it with this one so by replacing it with one that doesn't have anything in the search results the modal is going to go away because i'm not searching for anything anymore the button is going to or the input and button are going to appear again so that's not really a change and then this is going to run again so it's going to take the list of channels that i have in the database and generate the list here and because i now have something in the database this will actually populate with something so every time i add a channel it's going to re-query the database for the list of channels and that new channel is going to be in that list and the html generated is going to have that new channel so i'll go to views here and i'll query for those channels so channels and then channel dot objects dot all and i just want to return a template so actually i need the context so context and here i'll call this channels just like the name in the template uh channels here so channels and then i'll return render pass and requests we need the partials and channels.html pass in the context and this will return the html that we want so now let's go ahead and create a url for this let's go to urls we'll say add channel this will be views dot add channel and the name will be at channel but remember i'm using that channel id so i need to pass it here so we'll just say channel id here and this will generate the url for adding a channel with the channel id once i created here in the template so let's go over to the partial for the search results and what i want is anytime they click on the link for the title it's going to add it to the database so here i'll put htmlx post so this could be good but i'll use post and i'll need csrf exempt so let me just go ahead and do that right now so here is from django.views.decorators dot um csrf import csrf exempt so this just mean i don't need the crs crsf token when i send a post request to this ad channel and i misspelled something so views and now it's working so now i'll go back to the search results and for html posts what i'll do is i'll use the url for add channel and this time i'm passing something to the url so the channel id so i'll take the results dot snippet and i'll get the channel id so capital i lowercase d and that will give me the url for this particular channel when i want to add it to my database so before i can finish this i need to set a target for the results right so the results of this let's see views it's going to return channels here so i want to basically replace this channel section so let me just copy the id uh with the results from the view so i'll go to search results and i need to put this target somewhere so i could put target here so hx target equals channel section but because this is in a loop it's going to just generate this over and over again and since this doesn't change i can actually lift it up into another part let's say this top div and it will apply to everything inside of this div unless i override it with another target somewhere else so this target will apply to all of the html in here and i can do the same thing with swap so hx swap this just means how do i want to replace the target here so this target is a div and instead of replacing the inside i want to replace everything because if i replace the inside it's going it's just going to add the div channel section again and i'll have it twice so i'll replace the whole thing and you do that by specifying outer html so now this should be enough to add a channel to the database and then see the updated channel so let's go ahead and try this again so pretty print it is the first one i'll search for i'll click on the link and now we see pretty printed has appeared here so now let's search for another channel let's say tech with tim search and i can select tech with tim here and then it re-queries the database and injects the things in here let's search for one more let's say traversie traversing media i'll click on that and we see it appears so now i have three channels in my database if i open up my sql lights let me just create another thing here so sqlite video tracker uh db.sqli3 and i can select star from app channel and i see the data in my database so i was able to add those three channels successfully okay so before we get to the hard part of using celery to generate the progress bar and everything like that the first thing i want to do is i want to prepare my home page for the results so after this include what i want to do is i want to include another section that will have the results for the videos and if i haven't generated anything you'll see the button well the button will always be there but i can generate this as often as i want so what i can do is i can create another partial and we'll call this resultstable.html or let's just say results just like that it's going to be like the results section and once again let me bring in some html that i already wrote okay so this is what it looks like i'm loading human eyes for something that i'll use later and then we have a button here to generate the results and then we have a table that will have the list of results here so i have like channel name and then i have uh for full result and results and then i'll put the actual values here but this is enough to get us started so inside of index.html underneath the partial for channels i can include the section for the results so partials and then results right so if we go to the html now uh humanize is not a registered tag library that's pretty easy to solve we just need to go to the settings.pi and just add django.contrib.humanize like i said i will use this later but i'm just preparing for it now and now if i refresh this now we see the table and it doesn't have anything in it and then we have this generate button which will of course allow us to generate the results but we'll get back to that later so i'm going to start by querying for those results so if i just go here and i say like videos equals video dot object objects all and then i can pass this in the context so we'll say results videos because it's kind of a combination of the videos and the results yeah let's just say results here so passing these to the context and then it's going to generate the list and if i just make this like one two we should see that uh we have pretty printed appear a couple times and like i said this will be the actual data that we have in the database but we don't have that data yet so we can just prepare the code so now this seems to be a good time to work on the salary part where i create a task that will query the api and get all the results for me and then allow me to see this progress as is happening so i can create a progress bar and we can see it on the front end so to do this uh first thing i'll do is i'll create a file called tasks dot pi so this is where i have my salary tasks and let me just close out some of these until i need them again and now let me start bringing in the things that i need so first from celery i want to import shared tasks and this is what we'll use to mark a particular function as a salary task so we can run it and then from salary underscore progress dot back in i want to import something called progress recorder and the progress recorder will allow me to basically update the status of the apis so imagine if i have a thousand api requests in a loop so each loop iteration has one api request then i'll update the progress recorder by one so over time this would be like one two three four five until it gets to the thousand so this number will represent how much has been done so far like how many requests have been sent since that is the limiting part of this next i'll import my models i think i need both so import both channel and video and then finally i'll need to import requests because i'm going to send uh requests oh and i also need to import um the settings so from the configuration i'm importing settings so i have the youtube api key okay so now i want to create a function that is the task that is going to run outside of my web app so this is going to run in a background process and i want to be able to get the status of this background process as it's running but this needs to be set up in celery so it can run as a background process so i'll create the decorator share task and i'll put mine equals true i need this so i can get a self out of this so this isn't in the class but it's going to be getting something similar to self by using that bind so i'll call this git's video stats and it's going to take in self i want the first query for all the channels in my database and then i'm going to take all those channels and then query the youtube api using those channels so i can get um the information about the videos for those channels so the first thing i'll do is i'll say channels equals channel dot objects dot all and i'll get all of the channels and then what i want to do is actually i want to split this into two parts so the first part is i want to get the number of videos that all the channels have and then i want to get the actual stats for the videos so why do i need to get the total number of videos first before i get the video data well the reason is is for the progress i need to know how many requests i'm going to send before i start sending them so i can have an idea of how long it's going to take so with youtube i can send up to 50 videos at a time so let's say a channel has 1 000 videos that means i need to send 20 requests to get all 1000 videos the data for all 1000 videos and i need to know that there are a thousand videos beforehand so i know that there are 20 requests and then each loop i can just update the progress to be 20th and then as it loops over like as let's say 10 requests have been sent that means 50 of the requests have been sent so i know it's 50 done so that's basically the idea behind getting the number of videos first and then getting the actual videos second so to get the videos i need to construct a string with the channel id separated by a comment so what i can do is i can say channel ids is going to be uh a join that will add this comma and then i'll convert the well actually i don't need to convert anything so channel.playlist id because it should be a string and then this will be a list comprehension so yeah yeah this works so for channel and channels right so for channel and channels uh what i'm going to do is i'm going to return this playlist id and if i set this to a stir it's going to loop over like that well just to make sure that this channel is a string it should be a string but i don't need issues but yeah this is going to be um basically like a list comprehension i don't need the brackets but it works just like a list comprehension and then what i want to do is i want to send this request so let me bring in the url so url is going to be equal to this f string and this time i'm searching for playlists and then the id will be the list of ids so in this case it's channel ids and then the part that i want will be the content details so part equals content details and then the key is going to be the same as this so let me just copy this the key for the task or for the api call will be the same as what's in the views so and key equals settings.youtube api key and then i can send the requests so response equals requests dot gets and then the url and what i want to do is i want to kind of figure out how many requests i have to send so the way i can do this is i can say total request is zero so it starts off with zero and i'm going to loop over the results so for item in let's say response json and then items so this follows a similar pattern to the other responses for the api uh what i want to do is i want to get the number of videos so item content details and then item counts so this represents the number of videos in the playlist so like if this is a thousand then we know we need 20 requests if it's 100 then we know we need two right so what i want to do is i want to divide this by 50. so i can send up to 50 requests or 50 ids at a time so 50 video ids for the playlist i want to divide by 50 because i can get 50 results at a time so if this is 1000 divided by 50 i get 20. i want it to be an integer i don't want it to be you know decimal and i'll just add one so like if we have 49 then we do 49 divided by 50 we'll give you zero when it's integer division and then we plus one will give us one request and then it basically follows a pattern for all of them and this needs to be converted to an integer and then i'll just take the total requests and increment it by whatever this is so like i said if this is 2 000 then it will divide by 50 it will give me 40 and then plus 1 so 41. so for this particular channel it gives me 41 requests so if the first channel has 1000 and the second channel has 2 000 then i'll end up with 21 plus 41 requests so that's 62 requests so i need this like i said before so i know how much progress i've made now we can start the loop for getting the actual video data so here i'll loop over channel so for channel in channels i'm going to send a request so um we'll use url again because it should be separate from this actually i'll i'll make this a little more clear so um video api url just to separate it from this url and then i'll bring this in and it's going to take in the playlist ids so this time it's going to be channel dot playlist id so this is coming from my database and this is getting the items for particular playlists right so this part i'm getting the actual data of the playlist and for the results it's going to be the type of snippets and then for the um for the max results i want 50. so by default it's 5 but i want the max so max results equals 50. and then finally i need the api key so let me just copy this and add it onto the end here so now i have that url crafted and now there's another part to consider so because i might need to call this request multiple times i need to create another loop right and each iteration of the loop will go to the next page so i mentioned the next page token before like we go here and we go back we see next page token that will allow us to get the next page of results so by passing this token to the following requests we will get the next page so that's basically what i have to do here so what i'm going to do is i'll create a while loop and i'm going to send the request in the while loop so you know video response kind of separated from this response since i have two requests in the same function i'll do requests dot gets and then pass in this video api url here and then i'll get the results i'll just say video response equals results or results equals video response and then json and now this is where i want to loop over the results so we can say 4 item in results i want to basically get a list of the video ids so what i'll do here is i'll create an empty list we'll call this video ids right so i have the video ids here and then i want to append the video id from the playlist so what i'm doing here is i'm querying the playlist and i'm getting a list of all the video ids within that playlist right so i'm going to add those ids to this list here because i want to use these ids to query the youtube api one final time to get the actual data for each individual video so multiple steps here video ids dot append and i want to take from the snippet i want to take the resource id and the video id right so all these are going to be videos so i'm getting the video id and then i want to create a string so um video ids string it's the best name i can think of and then i want to join all the video ids okay so it's going to loop over the results from the playlist put them in this list and then i'm going to generate a comma separated string of video ids so i can send this to the next um request and just thinking about this i think i should call this playlist response right and playlist api url to kind of separate it from the videos so playlists here and yeah i think that makes more sense so the next part is i need to [Music] send a request to the api one more time within this loop and just looking at those loops you can tell it's going to take a long time and just note that there are better ways of doing this like you should use like an async approach to do something like this since you're sending so many requests and you're waiting on the responses to come back but for the purposes of this video i'm intentionally doing it in the more straightforward way of just using loops so it takes a while if i did it with async they will all take like half a second and that's not very interesting so with this this can take like 10 seconds it can take 30 seconds a minute however many videos i have from the channels that i add so just keep that in mind when you're thinking about what i'm doing in this video so here i want to create this video api url and i'll just bring this in and the id in this case will be this videos id string for the part we want snippet and statistics and then we need the api key so take this and just add it on to the end just like that and then we can get the response so we can say videos response is going to be requests dots gets and then this video api url and then we want to loop over the results so we can say for item in video response dot json and then we want to get the items here we're going to actually insert the data into the database so these are the individual videos and we're going to put them in the video here all right so titles views likes youtube id so let me just get all of those so we'll say video equals video and we'll start with title and views so title is going to be equal to item snippet and then title we have views views are going to be equal to item statistics and then view counts next thing we want we want likes so views items statistics this should be light counts let me just verify this let me go over to uh videos lists and then we can just see what this looks like the video resource and then we're looking in statistics okay so view count light count yeah we want the youtube id and i put views twice this should be likes so we want the youtube id so this is going to be item and then just looking here looking for the actual id for this so let's see content details maybe status statistics file details like i'm overlooking something so publish that oh the id is outside of that okay that's why it's a little confusing so it's not in the snippet it's just id just like that and then finally we have date published so date published will be publish that so snippet publish that item snippets and then published at just like that then finally we need the channel that this is associated with so channel equals and because we're looping over channel here we can just say channel equals channel and then that should be it for the video so i have a feeling something's going to fail here but we'll address that when we get to it so we just save that and then we need to deal with the next page thing so i can go down here and i can say if next page token so this will indicate to us if there is a next page so if this exists so if this is in the results so let's see we have the results here yeah so if this is in results i want to create another url for the playlist so playlist api url so i'll just put that here so i'm going to be updating this and notice how this is outside of the while loop so when this gets updated it's going to come back to this while and then send the request again using the new url so this is going to be almost exactly the same as this one and i think i copied too much so it's going to be the same as this one so we can just bring it in here the only difference is it's going to have this token so i can just add this on the end we can say page token is going to be equal to results next page token just like that and then for the else case where there is no next page token uh we're just going to break out the loop so break okay so this will add all the information to the database and actually let's go ahead and do this now because um i don't have the progress bar set up so what i'll do is i'll set up the endpoints you call this and hopefully it adds it to the database and if there are any errors we can fix that before we work on the progress bar so let me go over to views and what i'll do is i'll import this particular task so i'll go uh here so from dot task imports uh get video stats and then what i'll do is i'll create an end point and it's basically going to start that task so we'll call this generates and takes in requests and also no it doesn't need to take anything because of the way that i'm going to set this up so it's just going to get everything and since there's only one user i just want to delete everything so yeah what i'll do here is in the task before i actually create the videos um what i'll do is i'll just delete them all so um video.objects.all dot delete right so this way it's going to clear out the data first and then it will generate it so i have some invalid syntax here on line 15. uh here you know what i'll just make this a list just so i don't have any issues so this should work as a list comprehension um and then we have while so it looks like something isn't getting terminated before the while oh this should be wild true right yeah so now we have this f string the next page token um the playlist api oh this part uh results next page token i guess the brackets so what i'll do is i'll create a variable called next page token and then i'll just put it up here so next page token equals results next page token and now everything is working in so i'll go back to views i'll go to generate and the only thing i need to do here is just initiate the task so to do that in celery i just call the function and then dot delay right so this dot delay will mean it will send it to celery to run and then it returns a task for me and what i'll do is i'll render something so like i said with htmx everything needs to return html so what i can do is i can return the progress bar so partials progress bar.html i don't actually have this yet but i'll create it in a moment so for the context all i need to do is pass the task id and a value for the progress so the task id is going to be task.task id and i'll use the task id to get the status of the task right so every time i query the database looking for the task i need the task id and then that will return like how much of the task has been complete and i'll show you that momentarily and i also have a value so this is just the value of the progress bar and it will be set to zero initially so now let me go ahead and create this partial for the progress bar so progress bar.html doesn't return anything but it will eventually i just want to make sure that the task works so i can generate the results and then we'll be able to see it on the front end so i need to trigger this if i go to results i have this generate button and this generate button is what i want to use to trigger the process so here on the button i want to use an hx git and it's going to be the generate url so i haven't created the url yet but i'll create them in a moment doesn't have any arguments and then the target so hx target is going to be this so i call it table stuff that's not a really good descriptive name i'll just call it um results section and the target will be that i think i want a dash here that looks a little bit better so results section and then that and then finally i need the swap this swap will basically replace everything inside so uh inner html is the default so i don't need to add anything there so let me go ahead and create the url for this for generate so path generates and then we have dot generate and name equals generates right so that would just generate those video results for me so that should be it um let me just type progress bar here so we don't actually have the progress bar but like i said i want to make sure that the task works well so now i need to get celery up and running so there are two parts i need to start my docker compose stuff so i need to start rabbitmq and redis so i'll open up another terminal and i'll say docker compose up and i need to start my docker desktop so let me just do that so that's going to take a moment to boot while i wait what i'll do is i'll just delete everything from my channels because i added three channels and i want the first run to take forever so i'm just going to delete everything and then re-add one channel so delete from app channel and then now if i select star from app underscore channel we see there's nothing okay so now i should be able to use docker compose so i need to close that one and then start a new one and then docker compose up and there we go it's starting and i also need to start celery so to do that i need to start my virtual environment because i installed celery here i need to get into my project and then i can do celery dash a and then the name of my project which is which is video tracker and then starter worker and then i want the information to appear to be just info can run this and we know it's working when we can see the name of the function that we created in the tasks so get video stats here this is getting picked up because i have this decorator if i were to remove this decorator then this would just be like a regular function but once you add this decorator it becomes a celery task that you can call so i have this git video sets this debug task comes from the celery file that i had that's just to make sure everything's working and basically as long as you don't see any red here um everything is working correctly okay so now if i go back to views remember i want to call this generate it's going to start the process and get the task id and send it to the the contacts so what i want to do here is i need to add a channel because i remove the first one so i'll do pretty print it search and something failed so let's go over to the first one and it looks like there was just an error with the api so let me just go ahead and refresh this and hopefully that was a temporary error so search and yeah i'm thinking i'm having some momentary issues so what i can do is i can take a break and then come back when the api is working again it's i guess just bad timing okay it turns out i had internet connection issues but this should work now so we'll search for pretty printed and then i'll add it and now i'll hit the generate button and hopefully something happens so of course i don't see anything well probably an error somewhere so let's see it was posted to at channel but we don't see a post for generate so let me just make sure i refresh this and then hit generate and yeah nothing happens so let's see let's see what's going on so if i go to the console we have target error for generate so that's easy to fix and here i'm just missing the hash so now if i refresh and then do this okay so now we know it's scented uh we have this progress bar here so that's just the text that i put there which is just temporary uh we have the generate here and then if we go to the part with um celery we see all this red which means something messed up so let's see line 35 in get video stats so if we go down here to line 35 uh this is saying that this must be an integer right for the indices so uh for item yeah so we're looping over results oh i'm missing the items here so just items like that because this is the uh the entire results dictionary basically i need to get the items out of here so let's try that again let's hit generate go back and then we get the same error so line 35 so this definitely can't be the same issue so um we have items and results and then we're just saying here so what i'll do is i'll just print out what an item looks like so we can kind of see it oh actually i think i know what it is every time i make a change i need to restart the server the salary worker process so i'll just restart and now let me try to do this so generate okay so we have a different error so this is good so line 43 we have the video response so i just add an extra s there so let me go ahead and restart this generate again and now we have a validation error okay so this needs to be converted to a date so uh let's see where is it so we'll say um date published equals this and then we'll import from date times so let's see where is it where can i put this from date time import date time and then i can do date time dot string parse time and i just put the format so we have year year year i always forget these formats so let's see i always use a certain resource i'll just try this one okay yeah yeah i was thinking of a different one so we have percent y for the year somewhere and then the month is a number so percent m lowercase m and then the day which will be percent d okay so we have uh percent y dash percent m percent d and then we have the letter t and then followed by hour minutes or yeah hour minute and then seconds so capital h capital m so capital h dash capital m our colon here and then a colon after the m and then we have the micro seconds or the seconds okay so capital s and then the letter z right so this will parse this and create a daytime object and then we should be able to pass that directly to here even though it's a date field it should convert it properly so let's go ahead and try that again and see what other errors we get generates and this time it received it and because i have no errors that means it's running so i'll just give this a moment and we see succeeded in about eight seconds so if i go and run the same query uh we see i have the one channel and then from app underscore video i have a bunch of results so these are the titles of my videos i have a little over 400 videos that are public right now so we have the name so this is the oldest video that i have available um we have the number of views right so 6 800 views 23 likes here's the youtube id it was published uh november 19th 2015 and then it is associated with channel id number four okay so now we know that the task is working next what i want to do is i want to keep track of the progress so what i can do in here is i can instantiate the progress recorder so i can just do that here so progress recorder equals progress recorder and then you have to pass in self so it comes from here which comes from bind and then i'm going to use this uh progress recorder inside of the loop so if i just go to a place in the loop somewhere in the while loop i can put this at the end but basically i want to put it before this check to break out of the loop so i'll put progress recorder dot set underscore progress and i need um a couple of things so first i need the current iteration so i'll just put i and then i need the total so total requests right so each time this loop runs this i will increment by one i suppose the easiest way to do this is just to have like the i instantiated outside of the loop so i equals zero and then um we have i here and then somewhere i need to increment i and i can just do that right before so i plus equals one right so basically what this means is every time this while loop runs it's going to increment this by one and this represents the percentage done of the total so imagine this loop is going to run 50 times it will start off as one so it would be two percent one divided by 50 and then four percent six percent eight percent and so on and then of course if the total requests were to change for a different one then this would be different of course but uh this is all i need i can also put like a little pin if i need to debug this so for it iteration and then i okay so that's all i need to set the progress inside of the task so let me just close that now for the progress bar i need to get that uh progress as it's happening so if i go here i have the progress bar and it starts off with zero so let me go ahead and create that progress bar and once again i'll bring in the html for this and i'll paste it in here so this is pretty simple so we just have this progress bar um we have the the color so i don't like is warning let's go ahead and figure out a better color for the progress bar so what would be a good color let's use this green so is success so is success and then the value is going to be that number so it can be like zero um one this is going to represent the percentage done and then it just gets added here again um this is how it works you won't actually see that percent though and then we need to add in the hdmx stuff so for something like progress it's actually like a different uh technique as opposed to the other ones so the idea is this progress bar is going to get replaced over and over again right so first let's think where are we going to send this request so if i just do um i can put this on the container right so hx gets i'm going to send a git request to um you know get progress let's just call it that so url get progress and i want to pass in the task id right so i'm going to have a task id here and then i need a target so where is this going to go well what i want is i want this entire results section to disappear while the results are loading so i'll use this results section here and then finally what is the trigger for this so the trigger is actually going to be on load so this is what i was talking about how this is a little special so this is going to trigger unload right so every time this loads is going to send a request to get to get the progress and it's just going to return this again but i don't want it to happen immediately because that happens then it's just going to be a ton of requests and that's not going to be very helpful so what i can do is i can put a delay on this let's say i want to get this every 300 milliseconds so it's going to load it's going to wait 300 milliseconds and then it's going to send the request and then the response is going to come back with the hopefully updated progress bar with a new value and we'll see the value increment across the progress bar as it goes along right so now we just need to create this i get progress so we'll call this git progress it takes in a request and it's also going to take in a task id we need that task id to get the percentage done so what i need to do is i need to import from my salary progress library in addition to salary so we'll save from celery dots results we need to get async results so i can look at the status as it's happening and then we can import from celery progress back in progress so this will allow us to convert this async result into something that we can easily read so now inside of git progress we're going to get that progress so progress is going to be progress and then we're going to have the async results with the task id so we take the task id we use it to get the result from celery and then we convert that to a progress object so it's easier to read and then what we want is we want like let's say the percent complete right so we can use this and we can look inside of progress so progress get info and we're going to get progress and then uh percent right and this percent should be an integer i don't really care about like 2.5 it can either be two or three percent so i'll convert that to an integer and then what i want is i guess a simple way is i can say if percent complete is equal to 100 i'm going to do one thing if it isn't i'm going to do another thing there's also like an actual complete thing but since i have the percent here i'll just use 100. um what i want to do is i want to return the render for the the results section so we have results.html right and i need this results so we have um results equals video.objects and what i want to do is i want to order by like the views so order by uh views in descending order and i want the first 50. right because later we're going to have the infinite scroll but for now we just want the first 50. so we're just going to return this here so in the case where the progress is 100 uh what's going to happen is the progress bar is going to go away the results section is going to be replaced with results right and it should be um outer html like thinking about it right because let me think we have results section here we're going to generate it yeah so we want this to be the well we want it to be inner right and then the results section is going to come here so this is actually going to not be exactly what we want so i can come back to that in a moment once i think about that more and we see it working but uh we have the results here and then we're just querying from the database and this should be return and then in the case where we still have the progress going on what we want to do is we want to simply pass the the value and the task id back so task id is going to be the task id that was passed in and then the value which is what i called it is going to be percent complete right and then we can return render requests and then partials progress bar html with the context so here actually partials before results and just for consistency i'll use context here and then context here okay and then that should be it like i said i have to figure out the whole results thing in a moment but all this should still work so let's see if we can see the progress bar so let's go here refresh the page so i'm expecting that when i click on this button i'll see the progress bar and it's going to change as the progress happens and this took about eight seconds so let's see if it works and i already have an error so let's go back and see what the air is oh i need to create the url for git progress so path gets progress and then views get progress name get git dash progress so i'll make this a dash and now i should be able to do this generate and it doesn't work again okay so reverse for git progress oh we're missing the task id so if i go back to the url we have task id the slash and now let's see if it works so generate okay so we see the progress bar but we don't see any progress so um we see that the uh requests are getting sent it's just not updating the progress bar properly so let's go to views and let's take a look at get progress we have the progress bar we have percent complete we have the task id so we see that this is happening so let's see let's try printing out the task id and let's try printing out the percent complete so when i do this again we can see the data as it's happening so zero zero zero okay so that's obviously not good so let's see if everything is working in celery and it looks like it is so perhaps the the update's not working correctly so let's go over to the tasks and let's take a look so we have the progress recorder progress record is set progress we have i you know i plus equals one we have total requests which is here um so total request should be [Music] updated properly so let's see if this is being updated properly so total requests so let's initiate this we need to restart the server so initiate this and then let's look at the celery part and let's see if we can see the progress so here i need to restart the celery server because i just made a change to the task and perhaps that's the thing that i forgot to do in the first place so let's generate uh okay so yeah now it's working so i think it's just because i didn't restart the celery server like i said i'm so used to the automatic restart that it works but we see that the generate button works like the the the progress bar is going and then the results come so now let's update the actual results let's go over to results here and then um let's see so we'll have video name or let's say video title and then we have views likes date published so we have views likes um date published anything else views likes date publish um no the youtube id is going to come into play for the link so the first one is the channel so let's just do this five times so the first one is going to be the channel name right so this is going to be a video object here that we're looping over so results dot title and if we just refresh the page we see uh the channel name so it's actually the title should go here and this should be the channel so result.channel.name and now if we do this we should see pretty printed for all of them because it's only my channel okay so now for the rest of these i should be able to get the values so i have views we have likes and then we have the dates published so if i go here refresh okay we see views we see likes we see date published here it's not in the correct order because this is on the home page so i'll just do this order by and then go to the index just like that and now i see uh the views and this is where i want to bring in humanize if i go to results in this do for views we have ins comma and we can also do it for the likes as well and comma now we see likes okay so this part is working we see the date publish um last thing we want to do is we want to create a link to the video so just do this so this should be uh youtube.com slash slash watch v equals uh the video id so result dot uh what do i have here youtube id so result.youtube id and uh that should be it for the url so now we can click on this so getting started with the django rest framework we can open it here and we see that that's the video that we see so all these links should go directly to the video so that's it for the results now the next thing we want to work on is the infinite scroll so right now i only have the first 50 results but i have a little over 400 videos so i want to be able to use the infinite scroll and get all of those videos so once again htmx can do that so what i'll do is i'll create an endpoint so a view to return the next set of rows um get uh next rows and this is going to take in a request and basically all i need is the offset right so i'm going to offset by 50 each time so the first one the offset is zero the second one the offset is going to be 50 and then 100 and so on so i'll just say offset is going to be requests dot gets and then offsets uh this needs to be converted to an integer right and then we can use the same query here so results equals this but instead of the 50 here or instead of the zero here it will be the offset and instead of the 50 it's going to be offset plus 50. so this will give us like the next page starting from whatever the offset is and then we can just return our render request and then this is results.html well actually no this is not results so i need to create like a partial for the table rows so resultrow.html and let me bring that in okay so we're going to use humanize again and then we're just generating the table rows inside of the table that's all we need so we can take the the thing we have here and it's basically the same so let me just copy everything and paste it into results row right so we have the channel name and everything here and this will add it to the table once this is generated so this is going to be partial slash results row i want this to be rows instead of just row and then the context right so let me just change this to result rows and then for the context i'll have uh results results and then also i want to pass in the previous offset well actually the next offset so we'll just call this next then we'll do offset plus 50. so now i need to think how can htm x be used for infinite scroll so the way that this works is basically when we get to the bottom of the table so when we get down here we want hdmx to send a request to get the next 50 rows so how do we do that we use a trigger called revealed so if we go over to our results here what we want is we want a trigger revealed to be on like the last row of the table right so we can do that in django by um setting well before i do this i just noticed that i'm missing um a tr tag so we want this to be on tr the last row let me make sure there's a tr here here so um we want this to appear on only the last row right so we can put an if statement in here saying if my for loop is on the last iteration do something and then we have end if here and this is where i put the htm x up so hx gets i want to get the part for the let me put a space here so that's not confusing i want to get the next results so i'm going to send a request to git next rows which i'll create a url for momentarily but um if i go to results here this is going to be git next rows it doesn't need to take a value or does it yeah i'll put the query string so offset equals and then uh this will be the first one so the offset will just be 50 but for result rows it's going to be dynamic so this is the first generation of the first 50 results and then after this we have the dynamic one so i'll do that in a moment so uh that's the url inside of hx gets okay so the trigger is going to be revealed so when the last row is revealed we want to send the request so revealed so hx swap uh what this is going to do is it's going to add it after this row so hx swap and we can say after in so after the end of this particular row we're going to add in the results which will just be more rows so we have results after end and then that should be enough here and then it's basically going to be the same thing in result rows so let me just copy that and paste it here the only difference is this offset is going to be equal to um the value right or not the value but the value of the offset so offset there and then that should be that should be it so did i call it offset or did i call it next let's go to views yeah let's call this offset that's a better name okay so now let's see so right now creating a to-do list app was the last one oh let me add the url before i forget so path views get next rows and then we have the or this doesn't take anything we have views gets next rows name is going to be uh get next rows and so i can just copy this name make sure it is here yeah i use underscores i want dashes okay so let's see the last one is create a to-do list app so we'll refresh this um we have this issue because i didn't update the didn't update something correctly so let's see we have git next rows we have git next rows here we have git next rows here okay something just didn't update so now let's try to go and now it's doing the infinite scrolls so we can see i'm going much further down we're getting more results and then it ends at the video with the least number of views 1417 at the bottom and then it stops because there are no more results in the database okay so for the last thing i just want to be able to delete a channel so if i get tired of getting the results for this particular channel i just want to be able to delete it so let's work on the view first for this we'll call this um delete channel requests and then it's going to take in a channel id right so what i want to do is simply delete that channel so channel.objects.filter pk equals the channel id and then we just want to delete it right pretty straightforward and then we want to return a list of the new channels so um channels equals channel.objects.all and then we're going to return this partial here just like when we added a channel so we can just do that there i'm going to send a delete request so i need to have a crsf exempt on this and then for urls um we'll just have delete channel so path delete delete channel i need a channel id and then we have views dot delete channel name is going to be delete dash channel just like that so now we need to find where those uh buttons are so we can just go here and where is it each channel has a delete button right here is the delete button and i just want to send a delete request to uh this when it's clicked so hx deletes is going to be a url and then delete channel and then it takes in the channel id so channel dot id and that should be it so what is going to get replaced well if we go back up let's see we have the target let's see do we have a target uh no so like i said before this is in a loop so i don't need to add the target and the swap here so what i'll do is i'll put it on the section and then everything inside will inherit it so hx targets will be this channel section so channel section and then i want to just completely replace it right so i can say hx swap equals outer html so if i go here and do it now if i click this uh nothing happens so let's see what was the error context is not defined so go to views oh i forgot to add this to the context so we have channels channels so it should have been deleted already because it failed after the delete so we see that it's gone and then we see i have no data um because of the cascade so i'll search for a channel let's say tech with tim this time search tech with tim and then i'll add pretty printed again okay so now if i want to delete tech with tim tech with tim is gone if i want to delete pretty printed pretty printed is gone so that's it for the functionality before i go through one last run let me just look at that id thing again with the results so let's see we have the results section here and it was something like with the progress bar so what's being updated like this is replacing the progress the results here so inner html right but once the progress is done it generates the results again so it's going to be like results inside of results and i don't quite want that so let's see hx target results section inner html actually this is what i want yeah so this is going to delete everything inside of this div and replace it with the progress bar right and then once that's done it's going to replace it so i think one thing i can do is i can replace the outer html and just use results section here so it has somewhere to go when it's done right so results section here and then we have hx swap is going to be outer html so it doesn't duplicate it right so it's going to replace this with this here and then yeah yeah i think that works so let's go ahead and try it so let's add a couple of channels this time so pretty print it and then uh traversie and then generate so we see the progress bar progress is moving along it's taking a little longer because i have a second channel here so we'll just wait for this to finish he must have a lot of chat or he must have a lot of videos on his channel so that's why it's taking so long and i'm sure his will be at the top because he has a much bigger channel but this is almost done okay so we see here that um we have his videos and he has a few popular videos and then you get down to mine somewhere so we see traversing i probably should have used a different channel um so it's kind of mixed in but we see pretty printed here um pretty printed let me add tech with tim because he has a big channel as well then let me do this and i'll just wait but it looks like everything is working properly so i just want to see like videos from different channels interleaved but other than that like everything appears to be working properly with a zap and we see that i'm able to do all this without using any javascript at all it's basically just understanding the the patterns behind htmx and using those i can accomplish a lot of things on the front end with no javascript just using this library because it's it's pretty powerful it doesn't apply to every single case so you still probably need javascript for certain things that you want to do but the whole idea behind this is it allows you to do things that i you used to use javascript for but you don't have to anymore with htmx so now just looking at this we see tech with tim and traversing media they have videos together if i click on one of the links like this react crash course we see that it appears here so everything appears to be working well so that's it for this video i know it was kind of long but i wanted to show you all these things in htmlx i hope you were able to stay and watch all the way to the end if you have any questions about anything i've done in this video feel free to leave a comment down below if you like this video please give me a thumbs up and if you haven't subscribed to my channel already please subscribe so thank you for watching and i will talk to you next time
Info
Channel: Pretty Printed
Views: 14,894
Rating: undefined out of 5
Keywords: django, htmx, no javascript, docker compose, redis, celery, python
Id: vukzYCi2_yE
Channel Id: undefined
Length: 101min 57sec (6117 seconds)
Published: Sat Apr 09 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.