Django & HTMX - Dynamic Form Creation and Submission

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to see how we can work with multiple Django forms on a web page and how we can dynamically fetch extra forms as we need them using htmx we're going to see how we can submit these forms one at a time on the page and also how to submit all of the forms with one click and use htmx to submit that data and finally as we add these forms we're going to see the page update with the new database data shown on the page again all using htmx so let's get started now a lot of this content was inspired by this repository here it's a Django htmx to-do list I'll leave a link to this below the video and you can explore the code in this repository and what we're going to do is Achieve something quite similar in this video but we're going to use a contact list to do it so what we're going to do is go to vs code and we're going to start by going to models.pi and I'm going to define a contact model for this application this will inherit from Django's model and I'm just going to paste into a Fields here firstly the name which is a card field and also a card field is the phone number now in a real application you might want to use Django phone number field or something similar for the phone number but we'll keep it simple for this video we also have a thunder string method that Returns the name of the contact after we've done that we can run the make migrations command that will generate our migration file and once we've done that we can then run the migrate command to generate the database in this local directory so if we minimize this you can see we now have a SQL light file here if we now go to forms.pi what I'm going to do is create a form here called Contact form and that's going to inherit from the forms.modelform class and then we can define an meta class within that and we can link this model form to a model and of course that's going to be the contact model which I need to import from the local directories models file so let's import the contact and we can set the fields that we want to display in this model form we're going to use the contacts name and also their phone number as well so we want to display both of those fields that we had here within the model class and I want to add one more field to this meta class and that's our widgets dictionary and we're going to link both the name and the phone number to a widget and that's going to be a forms dot text input and that's actually the default for the car field but what we're going to do is provide a dictionary of extra attributes here and I'm going to provide the class of form control to each of these text inputs so let's copy this line to below as well and remember we need a comma here because this is a dictionary and we can use the phone number for the second key in this widget so basically what we're doing here for both of these text Fields we're adding a class of form control to the underlying input element and we can do that by passing a dictionary of attributes to the widget itself which is the text input now the form control class it comes from bootstrap and I have bootstrap loaded into this project as you can see here on line nine we have the CSS Ford bootstrap and we also have htmx loaded into this base template and we have an index.html we're going to work in this file here and this extends the base.html template so that's our setup for this video so now that we've added our model and the form class let's go to views.pi at the top here I'm going to bring those two things into this views.pi fill and then we'll Define an index viewer just below here so let's create a variable called context and what we're going to do is we're going to instantiate our form and we can do that and pass it to a form key so let's instantiate the contact form and then below that we can return the render function we pass that the request and also a template that we want to actually render here so the template we're going to render is just the index.html template and we'll pass the context to that template that's just this template here that we had at the moment that only contains an H1 tag so we're going to extend that now now to begin with let's just render the form with paragraph tags so we can see how that looks on the page let's run the Django server and we can go to the browser so this is how the form looks by default you can see that it's extended across the screen it's using that bootstrap form control class in order to show this form so what we're going to do is change up this form we want each form to appear on one line so we're going to use the bootstrap row class and we also need to create the form element itself so let's go back to our index.html and we're going to Define our form element and let's remove this form as paragraph and we can end the form element here so remember the goal of this video we want multiple forms on a page and we want one pair row on the page so we're going to Define that div with a class of row and this comes from bootstrap 5. so we're enclosing the form Fields within a row and for each field we want to take up four columns out of bootstraps 12 column grid system and on medium size screen we want to make it a little bit smaller and only take up three columns so we can use this div with a class of call 4 and call medium three and we're going to create one of them for each field in the form and there are only two Fields so I'm just going to copy this and paste it below and within each div we need to refer to the Forum field so I'm going to paste more code in here this is for the form.name and that refers to the contact forms name field and that's the name of the contact that we want to add on this form so we're rendering out the name field and we can add a label here which we can link to the form.name field and we're going to do the exact same for the phone number field so I'm going to paste some code just below here again we're using the form class and we're referring to the phone number field within the form and again we're adding a label and we're linking the four attribute of that label to the ID of our field so that's the two fields in the form they're added to a bootstrap row and the final thing we need to do here is add a button so I'm going to add another div here and we'll give that a class of column three and let's close that div off just below here and within the div we can add a button element and this button is going to have the text of submit and we can close that off within the button itself we want to give it some bootstrap styles for example button and button primary and we'll also give the button a type of submit here so this is the form element and eventually we want to submit this with HDMX and we also want to be able to get new instances of this form using a button on the UI so what I'm actually going to do is I'm going to cut the form out of this template and we're going to paste it into a partial called 4 form.html so this partial just contains the form element and all of the fields as well as a button to submit the phone if we go back to index.html what we can do is we can create another div here and we're going to give it an ID of contact forms and within that div we're going to use djangles include template tag and we can include that partials slash form dot HTML template and that will bring that form here into the parent template at this location so just as a sanity check let's go back to the browser and we can refresh this page and see how it looks you can see we have two Fields here for the name and the phone number and we also have a submit button now the submit button is floating above where we would like that to be so I'm going to add a bootstrap class to the div that surrounds the button so let's go back to form.html it's this div here as well as column three I'm going to give it a class of align self end and this is because bootstrap's column system uses flexbox and they align the attributes of bootstrap are applied to the Cross axis so if you use the default Flex row Direction then then what's going to happen is the alignments are going to apply on the vertical axis and when we align to the end what that means it's going to push it down to the bottom of that axis so let's go back to the page and hopefully that will fix this issue and as you can see the button is now displayed and it's more aligned with the input fields and I'll leave a link below the video to bootstraps documentation on using flexbox now what we want to do now is add a button below this form and that button should allow us to add a new form to this page and this is where we're going to start bringing in htmx into this tutorial so what I'm going to do is go back to vs code and we're going to go back to the parent template that's including our partial and just below this div with the ID of contact forms I'm going to paste a button and that's surrounded by a div just to give it some margin on the y-axis and this button has the text of add contact so let's go back to our page and we can see how this looks you can see it's been given the color of green and what we want to happen when we click this button we want to send a request to the server that's going to return that partial that we just created and that's the form.html partial this contains a form element and we want to add that into the Dom after the existing forms that are there so we have at the moment by default one form in the document when we click this button we want another form to appear below the above form and then every time we click the button we want to add a new form to the page so let's go back to vs code and I'm going to start adding some htmx attributes to this button so let's go to a new line here we're going to add in get requests with the HX get attribute and we're going to refer to a Django URL which I'm going to call create contact we're going to create this URL in a second but let's close off the URL template tag and remember the goal here is to add a new form after the existing forms if we go back to index.html we have the initial form here within this div with an ID of contact forms we want to add the new form into this div just below the existing form so I'm going to copy the name of this ID because we're going to use that here as the HX Target so let's set an attribute called h X Target and we can set that equal to that ID and we want the new form that's being returned by the server to be swapped into the Dom before the end of that parent element so we're going to set HX swap equal to before end and if we go to the documentation for htmx this is the HX swap page which I'll leave a link to below the video and this lists all of the possible values for this attribute and here we have the Ford end and this one inserts the response after the last child of the target element so if we go back to vs code we've got our Target of contact forms which remember is this Dev here when we first load the page this contains a single form which we're using the include tag to add when the response comes with the new form we're going to add it after this initial one so it's going to be added after the last child which is the above one that's going to be placed into this section of the Dome here so let's go back to our form.html that is the structure that we're going to have to begin with we're sending a get request to the create contact endpoint so we're going to go to urls.pi now and below the end index view I'm going to add a new URL here and it's going to be for a create contact View and you can see the name of this path it matches what we had in the HX get attribute so let's now go to views.pi we're going to create a view that's going to handle this request for us it's going to be called create contact and we're sending a get request to this at the moment later on we're going to send a post request to actually submit the phone so what I'm going to do for now is add a placeholder where we're going to check if the method is equal to our post request and if that's the case for now we're just going to pass and what we're going to do in the event of a get request is we're going to return that partial template so we're going to return the render function we'll pass the request to that and the template that we're going to use here it's partials slash form dot HTML and that partial is this one here that contains the form element and the new form that we're going to add to the Dom now we need to actually add this form to the context that we're returning here in this htmx response so we're going to create a dictionary here with a key of form and we can again instantiate the contact form and that will add that to the context so let's save this file and we're going to go back to the page now if we go back to our web page and refresh if we click add contact we see that nothing is happening here now this is my mistake you might have noticed this if you've been following along I've actually added these attributes to the submit button which is this button here now this submit button is what we're actually going to use to submit the form to the server when we click the add contact button we want to actually grab the new form so we should have added the HDMX attributes to this button here so what I'm going to do is go back to a vs code and we're going to remove the htmx attributes from the submit button and we're going to go to the index.html template and we're going to add these attributes to the add contact button let me tab that over so let's try this again if we go back and refresh the page when we click add contact you can see we get a new form displayed below and we can do this multiple times and it's always going to go to the server and get that partial template and swap it into the Dom within that ID of contacts list and it's swapping it in before the end so the new form appears after the last child and we can therefore add as many forms as we want now the next thing we're going to do is use htmx to actually submit the form when we click this button so what I'm going to do again is go back to vs code here and we're going to go to the partial template and to the form element what we're going to do is add an HX post attribute and we're going to set this equal to the URL template tag and I'm going to reference that same create contact URL that we just created and that we're using to send the get request to get the new form we want to actually submit the data to the same endpoint so when the form is submitted which is the default trigger for a forum element it's going to send a post request to this endpoint what we need to do now is go back to our viewers.pi file and we need to fill in this block when the request is a post request so let's create a variable here for the form and we're going to instantiate the contact form and pass the request.post data to that form or none that will instantiate the form and then we can use the form dot is valid function to check whether that's a valid form and if it is valid we can use the model forms save method to create the contact and save it to the database and if we've successfully created that contact we're going to add that contact to the context and we're going to return a new partial here going to pass the request and the template that we're going to return is just contact.html and we can pass that context to that so now we need to go and create this new partial that contains just the details for the contact so let's go to the partials directory and we'll create that file here and I'm going to keep this very simple we're just going to paste in a list tag here and that's going to contain the new contacts the name and also their phone number that have been submitted in that request now you might not want to create a partial template just for something as simple as this you can render that as a string on your Django view if you want but I'm just going to create a partial for Simplicity now the reason that we're returning an Ali element is because we want to swap this into the Dom and add it to an existing list of contacts so what we're going to do is we're going to actually fetch a list of all contacts in the view so let's go to this index view which is loaded when the page first loads and to the context I'm going to add a new key here called contacts and that's going to be equal to the model contact.objects.org so basically fetching every one of those contacts from the database so we now have this key of contacts available we can use that within the index.html template and we're going to add the list just below this button where we can click to add a new contact so let's define a UL element and we'll give it an ID of contact Dash list and within this unordered list we can create a template for Loop and look over each contact in that list of contacts that we've just added to the context and remember to end the for Loop and for each one of those we can use the include template tag and again we'll reference the partials slash contact.html which we've just created a second ago and that's this very simple partial template that just contains the LI tag again maybe Overkill to create a parcel for this I'm just going to do it this way to easily return a response from the server here with this render function so let's go back to index.html we're basically including An Li tag here for every contact that we have in this list of contacts so if we go back now and refresh the page that we have here now we can try and add a new user here so I'm going to try and submit this request and you see that nothing is happening but if we go back to the server you can see on the terminal we have this red text here the post request and above that it says forbidden the csrf token is missing now the problem here is we're trying to send a post request when we submit that form and that's an unsafe HTTP method we need a csrf token in that request in order to successfully send it now in order to solve this problem I'm going to refer to a page here and this is the Django htmx documentation this is a third-party library and I'll link this page below the video but basically this is the section that we're going to use here where we make htmx past the csrf token so we can copy this and add this to the body of our base template so let's go to base.html and this body tag here we're going to replace that and we're adding an each X headers attribute and we're adding the csrf token to the request that's all we need to do in order to get the post request working so let's now go back to the browser and go back to our application if we refresh this page and we try and add another user you can see the user has appeared in this page but they have replaced the form we no longer have the add contact form showing up so what we need to do is when we return this list element we need to change the HX Target in order to add it to the end of the unordered list tag rather than replacing the entire form so just to make that clear let's go back to vs code this is the contact list which contains all of the contacts that we have on our page when we submit the form and we get back a new contact we want to add it to the end of this UL tag so again we're going to use the before end swap technique but we need to do that on the form element after it's been submitted we're going to change our Target rather than applying this to the form itself which is the default if you don't specify an HX Target we're going to set it instead to the contact list and after that we're going to also set the HX swap attribute equal to before end and that's the same as we did before when we were actually adding new forms to the page this time we want to add the new contact before the end of this contact list so let's now go back and we're going to see if this is working we have at the bottom or existing contacts when the page first loads if we try to add a new contact here and submit that you can see the new contact is appearing Below on the list so that's dynamically added to the page when we submit this form and we can click the add contact button if we need to add any more contacts and again we can submit that and they appear below the page now you may have noticed we have a situation here when we submit the form the form itself stays on the page with the data filled in what we're going to do now is remove the form after it's been submitted so once we get the response back we want to remove the phone from the document so let's go back to vs code and we're actually going to use a new HX on attribute to do this and I've just done a video on this attribute I'll leave a link to that below the video let's go to 4 form.html and I'm going to create a new line here after the HX Swap and what we're going to add here is another attribute called HX on and the first parameter to this is the event that we're listening for in my case it's going to be the after request event after the event name we put a colon and then what comes after that is any actions we want to perform on that event occurring in the document and what I want to do is call this dot remove and the keyboard This refers to the element on which the HX on request was defined and you can find that in the documentation here and I'll link this page below the video too so let's go back to vs code and we'll quickly go over this when we send a post request to the server with our new contact we'll get back the data for that new contact and what we then want to do is remove the form from the document so that the user doesn't submit the form Again by accident once it's submitted we remove it so let's now go back to the page and we can test this out I'm going to add another user here and again we'll submit that and you can see that when we do submit that the form is now removed from the document and we can add a couple of new users here and again once we submit these it's going to remove each one of the forms one at a time from the document so we've seen here how to use the HX on attribute to remove elements from the Dom when an action occurs let's now take this a step further imagine we had multiple of these contact forms on the page but instead of clicking submit one at a time we wanted a button that allowed us to submit all of these forms at once what I'm going to do is add another button to the right of the add contact button that's going to be a submit all button so let's go back to vs code and it's going to be in the index.html template and we have the add contact button here what we're going to do below that is add a new button here and this is just a button that contains some text called submit all and an important thing to note is that we have an ID here called submit all we're going to use that ID when we're triggering a submit of all of these forms for now let's go back to the individual forms and remove the submit button from that form if we go back to the page now we can see that we have a different UI we don't have a submit button on the right hand side we have multiple forms and a submit all button now the question is how do we submit every single form on this page just by clicking one button so what I'm going to do is go back to vs code and this form element at the top this is what sends a post request for each one of the forms to the back end in order to create the contact in the database Now the default trigger for a form element is the submit event but of course we cannot submit this form anymore because we've removed the individual buttons that are tied to the phone so we can't submit them one at a time we want to submit them now with this button instead so what we're going to need to do here is Define a custom trigger using the HX trigger attribute and the trigger is going to be a click event but we want the click event to occur on this button here that we've defined with the text of submit also what we're going to do is copy this ID and we're going to go back to forum.html and we can use the from modifier and paste the ID of that button and what that's going to tell htmx is that when that that particular element is clicked it's going to trigger the post request to the server with the details that are in the form and if we have multiple of these forms on the page what this means is that when this submit all button is clicked it's actually going to trigger the submission for every single form that we have on the page and that's a good use case for this from modifier when you want to perform the actions based on an event that's coming from somewhere else in the document so let's now go back to the page and we can test this out and see if it's working I'm going to refresh this page and I'm going to add three contacts here so we've now got three contacts on the page if I submit all of these you can see that all of the forms disappear and all three of these users are added to the list below so now we have a single button where we can submit all of the forms to the server and then each one of those is going to return the response and it's going to swap in the list element after the end of the existing list so this is all Dynamic with htmx and again we can just add some more forms after that and fill in some more details here and again when we click submit all that's going to send that data to the server and add the contacts to the list so let's finish this video by adding a cancel button let's imagine we've added one form too many to this contact list and we don't need to add two instead we only need to add one we want to have a cancel button on the right hand side of the forms that's going to remove that phone from the Dom and again in order to do this we're going to make use of the HX on attribute and we're going to write some JavaScript and see the closest function that's defined in the document object model so let's start by going to vs code and in the form.html just underneath the two fields in the form I'm going to add another button with a class of button danger so that it will appear red and that has the text of cancel so let's go back to the page and you can see how this looks each time we add a new form we're getting the two Fields as before but now we have a cancel button on the right hand side now the goal here is that when we click the cancel button we want to remove the associated form from the document so what we're going to do is go back to vs code and on the button I'm going to add an HX Dash on attribute and the event that we're going to listen for on this button of course is the click event and when the click event occurs we can get a reference to the button with this keyword and then we can call the closest function to get the closest form element and what the closest function is going to do here it's going to start at the current element which is the button and it's going to Traverse up the Dom tree from the button in this case to the div and it's going to keep traversing up until it gets the selector that's specified as an argument here which is the form element so we go from the button to its parent div when it doesn't find the form it's going to go up to the div with a class of row and then from that div it's going to go to the parent form and it's going to find that element which matches the selector that we provided and then what we want to do when we find that form which is the closest form to the button we want to call again the dot remove function to remove it from the document so let's go back to the browser and we can test this out if we add a contact here when we click the cancel button it's removed from the document and again that applies if there's only one one form as well and we can add more contact forms here and we can specify new users and when we submit the forms that's going to submit all of them at once to the server and it's going to swap the new contacts into this list below so here we have a system that allows you to dynamically add forms to the page it allows you to fill in the form details and submit all of them to the server and we also have a cancel button that's integrated with the HX on attribute to remove these forms from the document when the cancel button is clicked so thank you for watching that's all for this video if you've enjoyed this video please like And subscribe to the channel would be greatly appreciated and we'll see you in the next video
Info
Channel: BugBytes
Views: 26,634
Rating: undefined out of 5
Keywords:
Id: XdZoYmLkQ4w
Channel Id: undefined
Length: 24min 35sec (1475 seconds)
Published: Thu May 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.