Django & HTMX App - Updating Data with HTMX

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi guys in the next couple of videos we're going to extend the functionality of our existing D jangle and HTM X application and we're going to give users the ability to do two things firstly we want them to be able to update an existing transaction and secondly we want to give them the ability to delete one of the transactions and that will come with a confirmation message using HTM X to confirm that they want to do that we're also going to add tests for both of these functionalities so we're going to dive into that in a second now I want to start by thanking everyone that donated to this goal that I have on coffee we're almost halfway there to that goal so it looks likely that by August hopefully I will release this jangle rest framework series on YouTube if you want to contribute there's a link in the description it would be greatly appreciated and thanks again to everybody who's donated already let's get started and go back to the application so let's look at the transaction list page to start the video what we have in the table of transactions is a column for various fields that we have on the transaction model and the rows in that table they represent transactions that we have in the database for this user who is logged into the application what we're going to do in this video is add a couple of icons at the right hand side of this table to allow a user to edit an existing transaction such as the one at the top here or optionally a user might want to delete a transaction from the database so we're also going to add a delete icon at the right hand side of this table as well now we're going to get these icons from this site here it's called hero icons and this gives you some SVG icons and this is made by the same people that made Tailwind CSS so I'll leave a link to that in the description of the video we're going to search for some icons now so in order to edit an existing transaction I'm going to search for the word edit and that's going to give me some results here I'm going to copy the SVG from this one and I'm going to copy that and go back to vs code and what we're going to do now is in vs code we're going to open the template where our table is displayed so that's in the tracker application in jangle and within the templates directory we have a partial called transaction container. HTML so I'm going to go to that and if we scroll down just below here we can see the header for the transactions and that matches what you can see on the page here this header that we have and then below that we have the table so this is the table here it's on line 54 of this code and by the way the code for this video is all on GitHub we have a link in the description now what we want to do to this table is we want to add another column currently we have four columns and these are represented by th elements in HTML what I'm going to do is add another column here and this is going to be an empty table header so I'm just going to leave it blank it's going to be another column in the table but it's not going to have any text in the header and then what we're going to do is go down to the table body and what we have for each transaction in this for Loop is we are rendering the data associated with the transaction we're going to add another table data element now so just below this last table data element we're going to to paste another one in now and I'm going to paste the SVG element we've copied from hero icons into this column now below that I want to also add another SVG icon so the one that we have is for editing an existing transaction let's go back to Hero icons and what we're going to do is we're going to search for an icon that we can click and it's going to allow us to delete a transaction now if I type delete in here you can see what we get back below and what I'm going to copy is this one here it's the X Circ icon we're going to copy the SVG for that and let's go back to the template and back to the table and I'm going to paste that in here so now we have two svgs within this table header what I want to do now is go back to the template and we're going to refresh this page and we're going to see how this looks now that doesn't look ideal but you can see the icons that we want to actually click in order to perform these actions the one at the top is the one to edit an existing transaction and the one below is for deleting a transaction so I'm going to add some classes to this particular table data element in order to make this appear on a single line and we're going to use flex in order to do that let's go back to the template and it's the table data that's the parent of these svgs we're going to Target that and we're going to add some classes from Tailwind let's start with the flex class and we're also going to add items Das center now what the flex class is going to do is it's going to align the items along the row axis so in other words instead of appearing top to bottom as you can see here it's going to appear left to right on the row axis and what item Center is going to do is it's going to align it on the y- AIS so in other words if I refresh this page you can see now that the icons are left to right so on the row axis and they are centered on the cross axis as it's called in flex boox and this means that they are centered along that cross axis now what I also want to do here is add a bit of margin to the right hand side of the first one of these icons so let's go back to the template and we're going to look at the very first SVG element here and I'm going to scroll to the right hand side you can see that it has a class attribute and we're going to add another class from Tailwind margin wr-1 and if we go back here and refresh you should now see that we have that slight extra bit of margin here on the page between the two icons now what we're going to do next is surround each one of these icons with an anchor tag and that's going to mean that when we click the icon it's going to link us to a particular page for in one case editing the transaction and in the other case deleting the transaction and we're going to do this using HTM X so let's start in this video with the link to edit an existing Transaction what we're going to do here is start by adding a URL for editing a transaction so let's go back to VSS code and we're going to go to urls.py and we currently have three URL patterns I'm going to add another one now so let's add this just below here and it's going to be/ transactions and then we're going to embed a path parameter here for the primary key of the transaction that we want to edit and then we're going to also add the update to this particular path and when we send a request to this URL we're going to call a view that we're going to call updator transaction and let's give that a name so that we can reference it in the Django template and it's going to be given a name of update SL transaction so that's our new path you can see that we're embedding the primary key for the transaction that we're actually wanting to edit and that's important because in jangle we need to actually know which transaction we are changing so we can fetch the details and then update those details so that's the URL we now need to write the view called update Transaction what I'm going to do is go to the views.py file and let's go to the bottom and I'm going to Define another function based view in jangle that's called update transaction and that is going to take the primary key as a parameter and that's because when you create a jangle URL that contains these Dynamic path parameters what Jango is going to do is it's going to pass those path parameters as parameters to The View function so this is called primary keyy because it's also got the same name within the path we can then get that in the function and then we're going to use that to actually look up the transaction that we're going to edit now what I'm going to do is get the transaction from the database and we're going to use a buil-in function in jangle called get object or 404 and that function will take the model that we want to fetch from the database along with any lookup parameters so the lookup is going to look up by the primary key that we have in the URL and that's passed through to to this function so it's going to look in the transaction table in the database and find the transaction that has that primary key what we also need to do is actually import this function so let's go to the top and this is defined in the shortcuts module in jangle so as well as render we're going to import get object or 404 now let's go back to the view function once we have the transaction if we found a transaction in the database with that primary key we can continue writing this function so what I'm going to do for now is Define an empty context now we are going to add some data to that context very soon in the video but for now let's return render and that takes the request as a parameter and what we want to return here because we're going to use HTM X is a template partial that's going to contain a form that's going to allow the user to update the existing transaction so I'm going to paste the name of that partial in here and it's going to be in the tracker / partials directory of the templates directory and it's going to be called updat transaction. HTML and finally we also are going to want to context to this so let's add that as the final argument to render let's now create a blank template called update transaction so I'm going to go to the templates folder of the tracker application and within the tracker SL partials directory we're going to create another partial template here called updat transaction. HTML and in this we're going to display the form that we want to return from the HTM X request and that form is going to have the same Fields as the form used to create a trans transaction and that makes sense when you create a transaction you specify some data and when you update the transaction you're probably going to have the same Fields you're going to want to update the same data so what we're going to do now to start with is go to the create transaction. HTML template and I'm going to copy the divs for all of the fields that we have in the form and we're going to extract these to their own partial as well so let's start with the div for the form. type field so that's a transaction type and we're going to copy all of the divs for all of the fields in this form and let's cut them out of the create transaction template and then in the partials directory we're going to create another file here and this file is going to be called transaction D form. HTML and we can paste these divs into that form and also because we're using template tags from Jango widget tweaks we need to load these so let's go to the top of the template and use load widget tweaks so now we've extracted all of the fields for the create transaction and update transaction forms we can now include these in these templates here so let's go back to create transaction and we're going to use Jango include template tag and what we pass to that is a reference to the template that we want to include and that's the new transaction form. HTML that contains all of the common fields for both of these forms once we've done that we can copy all of the code that we have in the create transaction. HTML template and we're going to bring that over into update transaction and what we can do at the top is update the header so instead of Crea transaction this is an update page so we're going to change it to update transaction and we're also going to change the HX post attribute on the form element instead of create transaction we're going to send the post request to the new update transaction URL so if we go back to urls.py here you can see this is the one that we defined at the bottom that links to the new view in the application so now we have an update transaction page and again we're including the same fields from the form that we had for creating the transactions so the Reon reason that we've broken this into its own template partial is because it's common to both of these forms so we now have the partial defined for the update transaction page what we can do now is go back to the parent template and that's the transaction container and let's go back to these two SVG elements these represent the icons that we see on the page when we click this edit button we want to use HTM X to send a get request to the back end and to load up the form that allows us to edit a transaction and that edit form should be automatically populated with the existing data for the transaction so let's go back to the template and the first SVG element that we have here we're going to surround this with an anchor tag and we're going to use HX get this is an another HTM X attribute that's going to send a get request to the URL that we provide here we're going to use the URL template tag in jangle and again we're going to pass the update transaction URL now one thing to note about that if we go back to urls.py it takes this Dynamic primary key parameter in the URL so we need to pass that in here and the way to do that with jango's URL template tag is simply to reference a primary key here now if we look at the template for Loop this is iterating over each transaction in a query set so to pass the primary key we just reference the transaction and we get the primary key attribute from that transaction so that will then add this to the URL that's expecting a dynamic primary key and that's going to therefore send a request to this URL which is going to then call this view function and pass that primary key in and that comes in as a parameter and it allows us to look up the transaction from the database so we're passing the primary key in here I'm going to add some other HTM X attributes let's add HX push URL and set that to true and we're also going to set an HX Target here and that's going to be set to an element with the ID of transaction d block and the final thing I want to add to the Anchor tag is a class from Tailwind CSS and that's the cursor Das pointer class so we now have the anchor tag and I'm going to wrap this around the SVG element we can now show how this works on the page so let's go back to the application and for the very top transaction I'm going to hit this button and it appears that nothing is happening so let's go back to the vs code terminal here and you can see we have a no reverse match problem and the reason for this is because we're calling update transaction in another place but we're not passing the primary key so if I go back here to the new update transaction form we have referenced this URL but we're not passing in any transaction primary key now what I'm going to do at the moment is actually just comment this out and we can also comment out the form at the bottom here when we close the element and let's go back here and try this again so I'm going to refresh this page and this time when we click the edit button we are taken to this page now we're not seeing the form but we are getting a new partial here so why are we not seeing the form let's go back to the common partial that we've extracted here and it's called transaction form this is referencing fields that exist on a form object but we have not added that form to the context so what we need to do now is add the form that we need to the context so that this template has access to that and can then render those fields on the page so what I'm going to do is go up to the create transaction View and this is the form that we used to create a transaction and it's going to be the same form that we need to actually update a transaction as well so I'm going to copy the name of this form and let's go down to our new view here and to the context I'm going to add a key of form and that's the one that we need in the template to render the fields and we're going to instantiate the transaction form but this time we're not going to send the post request data what we're going to do is send a parameter called instance and we're going to set that to the transaction that we got from the database on the line above now this transaction was retrieved by looking up the transaction objects by the primary key we got back a transaction and what happens in D jangle if you attach an instance to a form then the data from that instance is going to populate the fields of that model form now we're going to see an example of that in a second what I'm also going to do is add the transaction itself into the context so let's do that now by referencing the transaction variable the reason we need the transaction of course is because we've commented this out what I'm going to do is bring this back in and we need to reference a transaction primary key when we actually send the post request to this URL the UR needs the primary key so that it knows what transaction it's got to update so now that we have a transaction in the context let's add that primary key to that URL and let's go to the bottom and again we're going to uncomment this here so that the form is properly closed off now I want to try this again now that we have a form in the context we should be able to reference these fields and we can also send the post request and reference the transaction primary key let's go back to the page and I'm going to go back to the list page here and refresh the page and if we click the edit button this time we're taken to an update page that contains the form with the right fields and this time unlike the create transaction form the fields are actually already populated with the existing data for the given transaction so let's say I wanted to change this transaction from an income to an expense and instead of 444 let's put 7777 and these are randomly chosen numbers once we click add what we're going to see here is that we're sending that request and we're getting back the exact same template template and that's replacing the form element now at the moment we're not actually handling the post request that's sent when we update a transaction so we need to write the code to actually handle the post request and one other thing I want to do here is change the button text from add to update so let's go back to the update template here and we're going to change this button text to update and now we can go back to views.py and within this view function we're going to handle the post request so let's go to line 54 here underneath where we fetch the transaction we're going to check if the request. method is equal to a post request and what I can do here is go to the create transaction code and we're going to copy a lot of this so I'm going to start with the code here from where we instantiate the form so I'm going to copy all of that and let's go down to the function that we have and I'm going to paste that in here and fix the indentation so we're going to change this a little bit if it's a post request we're going to create a form instance and we're going to pass the posted data into that but we also want to attach instance as we did before so I'm going to pass instance equal to the transaction that we pulled out from the database and what this is going to do when we pass an instance like this when we actually save the model form it's not going to create a new row in the database it's going to update an existing instance so let's go to the jangle documentation on this now this is about the model forms save method so every single model form in jangle has a save method and this method is going to create and save a database object from the data that's bound to the form now this is the important line here for what we're doing at the moment a model form can accept an existing model instance as the keyword argument instance and if this is supplied the save method is going to update that instance otherwise if you don't pass an instance what it's going to do is create a new row in the database for the given model so let's go back to VSS code we want to update an existing instance so we're going to pass that in as a parameter and then we're going to check if the newly posted data for the form is valid and if that is valid we can then just save the transaction and we don't need to worry about associating the transaction with a user because if this is an update the transaction should already be tied to a user so we can just call transaction Dove or sorry we can just call form. Save and then we can change the message that's being returned here so I'm going to say transaction was updated successfully and we can then return the success partial when the transaction has been updated now let's add an else block if the form is not valid what we're going to do is copy the context that we had below here and I'm going to paste that in here and tab it over so if the form is not valid we want to add a form key but we want to actually add the form that's been submitted from the front end in the post request so we populated that form with the posted data and we're tying that to an instance we want to pass that in as context if there are any errors in the submitted data and we also want to return the same partial as we return on a get request so I'm going to copy this statement and we're going to paste that in here now we want to retarget the response because we're using HDMX and the reason for that is we don't want this duplicated header so we're going to copy the code that we wrote in the create transaction function where we got a response by calling render and then we called the retarget method from Django HDMX so let's copy that and we're going to go to the bottom here of the else block I'm going to set this as a variable called response when we call render that's going to return that rendered template and then we can run return sorry the retarget method we pass the response to that and the second parameter is the actual element that's going to be the new Target for the response data so let's go over this one more time so that we understand what's going on when we send a post request we've already fit the transaction from the primary key in the URL and we then tie the form instance to that transaction so that we're going to update the transaction when we send this posted data so we have a form with the new data that's being updated by the user if that form is valid we're going to just save the transaction and that's going to update that Row in the database and we return the success partial in that case otherwise if the form is not valid we go to the else block here and we add the invalid form to the context and we return the same partial update transaction and that's going to then allow the user to take the invalid form and change that and submit it and hopefully when they submit the form again they will get a valid instance and we're using the retarget method as we did in the create transaction video in order to retarget the response data when we have an error so I want to test this out now so let's save views.py and we're going to go back to the my transactions page and to the top transaction I'm going to go to the edit form and we're going to change this to an expense and let's change the amount to 222 when we click the update button you can see we get back the success partial here and the transaction was updated successfully now if we click the view transactions button here you can see at the top that existing transaction has now been updated so it's now an expense it's not an income type of transaction and we also have an updated amount of 222 so we can do this with any number of these transactions so if I wanted to change this one we could click update at the bottom and then you can see that's reflected in the table when we go back to the list page now just to finish this video I want to add one test to our code so this is going to be a pie test function that's going to allow us to test test this update functionality let's go back to vs code now we were writing the tests within the test directory and we have a file layer called test views.py I'm going to open that file and we're going to go to the bottom here now the test for this is actually going to be very similar to the test that we wrote for adding a transaction so what I'm going to do is copy the code for test add transaction request so let's copy the code for the first three lines here and we're going to go to the bottom and paste these in now if you're not familiar from the previous videos this py test mark this comes from the pest jangle package or rather it's a plugin for py test and when we add this to a function this decorator it's going to tell py test that the function is able to it's permitted to access the database now we're calling the function test add transaction request I'm going to change this and we're going to call it test update transaction request and we're passing some pest fixtures into this function here so we have a user and we also have transaction dictionary param now if we go to conf test.py we can see where this is defined and what this returns is a dictionary of data and this dictionary can be added to the data that's sent in that update transaction request and we're going to do that now using the py test jangle Cent and that's the other fixture that we've injected as a parameter here so you can actually pass any number of fixtures to a py test function once we've done that we're going to take the user that's tied to the transaction and we're going to log them in using the client. for login function and then what we're going to do is create an assertion and we're going to assert the transaction doobs do filter and we're going to filter the transactions to only those that are tied to the user that we're passing in as a fixture we're going to check that the count of these is equal to one so basically this assertion is asserting that the user that we have here has exactly one transaction in the database and the reason that they have that is because we have this statement here using Factory boy we've created a transaction that's tied to the user and that will then save that to the database so we make that assertion here in the test function and that just makes sure that when we prepare this function we have the expected conditions and we can now write the bulk of this actual function just below that assertion so what we're going to do to start with is grab the same transaction from the database we can do that by calling transaction. objects. first and now we're going to try and update this transaction so what we're going to do here I'm going to add a comment we're going to update the transaction that we have via a post request and we're going to mutate the dictionary parameters that we have here from the pest fixture now remember this transaction that we have that we're getting back using this function this is equal to the transaction that's been created by the factory boy class so this P test fixture is creating that transaction and it's returning a dictionary of data for that transaction now we want to change this so we need to change the actual keys for some of this data in the dictionary so in order to do that let's go back to the test function and what I'm going to do to the dictionary is mutate two of the keys so for the amount field we're going to change that to 40 and for the description field we're going to change it to a new description so what we now want to do is use the pest jangle client and send a post request and this post request should be going to the new update transaction URL so in order to get that we can use the reverse function in jangle and we can pass the name of the URL which is update transaction now this remember it takes a primary key so we also need to pass keyword arguments here and that's a dictionary and the keys for this dictionary should match the fields that we have in the URL so the primary key was the only one for this endpoint and that's going to be the transaction. PK so the transaction is this one here on line 131 when we want to update the transaction we need to pass the primary key in the URL this is how we do that with the reverse function in jangle and while I'm doing this I should note that the keyword arguments should be part of the reverse function so let's add them here so this defines the URL that we're sending the post request to what we can do now is we can actually add the transaction dictionary parameters as data to this URL so when we send a post request it's going to send the new data and what this should do is update the transaction that we have here on line 131 with the new data that we're sending here so now we're going to check that the request has updated the data and it's not created a new Trans action in the database so we should see the same count of transactions as we had at the start of the function so let's copy this assertion here and we're going to paste that just below we're asserting that the count of transactions the number of transactions I should say attached to this user is still equal to one we can then grab the same transaction from the database by calling transaction doobs do first and we can now check that the amount is equal to 40 so how do we do that we can create an assertion here transaction. amount should be equal to 40 and we can also make the same assertion for the description field so the transaction. description should now equal this string here so let's paste that in here and we can make sure that that has been updated as well and that's basically all we need to do in this function what I'm going to do is stop the jangle development server and what I'm going to do is run the P test command and that's going to run our new unit test now you can see we're getting an error here and that's due to a mistake that I've made the transaction object has no attribute description so what we're going to do is go back to this function here if we look at the conf test.py file you can see the data that we have in the dictionary there is no description key here what we're going to actually now change is the date so I'm going to go back to the test function and let's change this key here to date so that we can change the correct field in the dictionary and I'm going to remove this and just above this I'm going to create a variable called now that's going to be equal to the datetime do Now function in Python and we're going to pass that time in as the new date time and what I'm also going to do is I'm going to convert this to a date object in Python so we can call do date here and this is going to give the date at the time at which the test was run so we expect the transaction to be updated to that date and what we can do just at the bottom here is we can Check transaction. Dat and we can make the assertion that it's equal to that variable now if we then expand the terminal and clear out the failed test what we can do is Rerun pyth test and hopefully this time it's going to pass it's collecting that and you can see now we're getting that green output that means the tests have passed so what I'm going to do is just summarize this function what we've done here is we've got a function that takes a user as well as a dictionary of data that we can use to create and update transactions and we also take the client from pest jangle which allows us to send requests we're then going to log the user in and that's important because you can't update or create a transaction that's tied to a user and unless the user that's sending the request is authenticated so we log the user in and we make sure that user only has a single transaction in the database we then fetch that transaction using the objects. first method and because this is a test database there's only going to be one transaction in the database here because we've not created any others once we've done that we update the transaction using a post request so we change some of the dictionary parameters that were used to actually create the transaction to new values and then we send a post request to the update transaction URL and we add the primary key to that and that tells D Jango which primary key which transaction it needs to fetch and then update in that request we then add the parameters to the post request so it sends the new data that's going to update the transaction instance on the back end once that's done we can create some more assertions so first of all we want to assert that when we send that post request we're not creating any new transactions in the database because this is an update we should only be updating an existing transaction and not creating any new ones and then we get that transaction back from the database and we make the assertions here to make sure that that instance has been updated with the new values so that's nearly all for this video but I want to highlight one last thing that I think I've missed in this video If we go back to views.py I think we need to pass another look up here to get object or 404 and we also need to add the login required decorator so let's start with that we're going to copy login required and we're going to bring that to the view function and add that here and that means that when you send a request to update transaction you have to be logged in authenticated in order to do that now I think in new versions of Jango this login required functionality is actually going to be the default for views if you're interested I could make a video on this topic but we are adding it here just to be explicit that this is a view that requires authentication and then one other thing we need to do here is in this line of code because we're fetching a transaction we can add the primary key to that in order to do the lookup but we should also be making sure that this transaction is tied to the user who's authenticated otherwise you could potentially be sending a request to this endpoint and all you need to do is add the primary key to the URL and it's going to send that update and it's going to change that in place no matter who the user is that actually owns that transaction now we can add a look up to the user by adding this here to the get object or 404 method meod we are looking up now and we're going to see that the user is actually tied to the authenticated user and this means that if some other user tries to update your transaction this lookup is now going to fail because it's not going to return anything so it's going to return a 404 not found instead so this is actually really important for when you do these lookups you need to make sure that not only are you looking up by primary key but that the given transaction is also tied to the authenticated user so I just want to make sure that still works with the test Suite so let's go back to the terminal and rerun P test just to make sure that this is still working and you can see that all tests have passed and if you run the jangle development server and go back to the page what I'm going to do here is try and update the transaction at the top so let's try and change it to 555 when we do that you can see we get back the updated data in this table so that's all for this video what we're going to do in the next video is we're going to implement the delete transaction functionality so as well as being able to update existing transactions we also want to give users the ability to delete transactions from the table so that's coming in the next video thank you for watching this video if you've enjoyed it give it a thumbs up and subscribe to the channel and if you're able to donate to the coffee campaign that we have there's a link in the description thanks again for watching and we'll see you in the next video
Info
Channel: BugBytes
Views: 1,760
Rating: undefined out of 5
Keywords:
Id: B3DHE9RIimY
Channel Id: undefined
Length: 32min 52sec (1972 seconds)
Published: Mon Jun 24 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.