Django and HTMX #7 - File Uploads and Film Detail page

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi there and welcome to another django hdmx tutorial in this tutorial we're going to build out a film detail page and we're also going to give users the ability to use htmx to upload images and associate them with films so how we're going to do this we're going to let the user click the films in their list and we're going to use htmx to load up a new partial such as you can see on the screen now this loads up the film detail page for this film we haven't got a lot of information for each film so we'll display a very small template here we can build this out potentially in future tutorials but we also give users the ability to upload a photo if it doesn't exist we'll show a message but we can then say upload a photo no country for old men and then when we click the upload file button this is going to use htmx to dynamically upload the file and associate it with the film and then automatically without any page refreshes from the user it's going to return the new partial as you can see here with the new image now obviously not a css tutorial so we could make this look better but the gist of it is we're using htmx to drive a single page application where we can go between the list view and the detail view and we're also using htmx to upload files so let's see how to do that now and dive into the code so let's start by creating the detail view for the film page this is going to allow us to click a film and we're going to click the text here that says the film's name and its order number when we click that it's going to load up a new page using htmx that contains details about the film and will eventually allow us to upload a file an image to associate with that film currently if we click nothing happens so let's start building this just now if we go to vs code and we're going to look at the film list.html what we're going to start by doing is enclosing the text here that contains the film's name and it's order we're going to enclose that in an anchor tag and we're going to give that anchor tag some htmx attributes so first one is going to be hx get this is going to send a get request to retrieve the detail template and it's going to go to a url we'll create in a minute called detail and we're going to pass to that url a primary key parameter so that it knows which film that it's going to render we're also going to pass an hx target and that's going to be equal to the film list the reason for this is that when we return html for the detail page we want to swap it into this film list id in the dom so now we're going to actually create the url for this detail if we go to urls.pi we can copy that into the htmx url patterns and you see that it accepts that path parameter for the primary key here we can now build the view detail that will allow us to deal with this request so detail it's going to take the request and the primary key and let's protect that with login required now what we're going to do is we're going to try and get the model instance with this primary key and we're going to use the get object or 404 method from django that takes as a first parameter of the model and it's a user films model and we're going to look it up by the primary key get object or 404 is a django shortcut function you can import it from the same module as the render function so that will either return a film instance a user film instance or it will return a 404 not found error so with that we can now create a context variable where we attach the user film object to a variable called user film and finally we can render a partial i'm going to copy the code for this so we have a partial here called film detail this is new we're going to create that now i've already created an empty file here for now we're going to just render out hello so let's see what happens when we do this we should now be able to click the text because we've wired up an hx get attribute to that and it's going to return html that we're going to swap into the id of film list let's see what happens and we get back hello one problem with this is the detail page should not show this stuff at the top here so i'm going to move that into the film list.html to do that go to films.html and this content here from the div down to the hr just cut that out of there and go to filmlist.html and we'll paste that right at the top of this file just then here and what that will do is that will move that away from the root component the film component and when we click a film now we get hello back so now that we have that what i'm going to do is make this template a little bit more interesting we don't have a lot of data on the films because if you look at the models we only have a name and we have the order here as well so let's now go to the template the film detail template and i'm going to paste in some html here all we're doing is showing the film's name in an h2 tag and we're showing a paragraph that contains some information about the film at the bottom we've got a button but i'm going to remove this for now so if we refresh this page and we click fargo we should now see we have like a detail page and this is kind of replicating a single page application you'd build and react we click something and we go to another page and what i'd like to do is add that button back in that will allow us to navigate back to the page we had before which was this page currently we can go from here to the detail page but we can't go back so let's see if we can add a button to do that let's paste that button back in here and we'll move down these attributes so we've got an hx get request that is going to another url called film list partial let's create that url now this url is going to be responsible for returning us to this page here so let's try and build that now within urls.pi we'll add another url and i'll paste that in and it's now going to load a view called films partial which i'm going to create in views.pi so login required protect it again within the films partial we're going to get all of the users films and this is going to be a similar query to what we issued up here where we filter user film objects by the request user so let's put that in here and finally we're going to return the film list.html populated with these films and we can just copy this render here to do that so return render and that should now return us to the page now there may be some extra steps needed here but let's see if this works if we click pulp fiction we're not actually seeing the button i think i need to save the film detail page here we've got the button um so this will go to the film list partial view that we've set up which is here and that's going to return the film list that's populated with all of the films in our list and that's going to swap that back into the film list so it's like a single page application with a root element in this case the root elements the film list id and when we go to the detail page we swap out the html response into that film list and it's the same when we go from the detail page back to the film list we get back the list of films and the template and we swap that back in so let's see if this works now if i refresh the page click fargo you see we get the detail page and if we click the list component then we go back to this and we can toggle between different films and the list components and the detail components so that's pretty cool and it's pretty useful as well now what we're going to do is build out the detail page a bit by adding the ability to upload a file so if we click fargo we want to see a poster or an image that will show us some details about fargo let's see how to do that now now in order to upload an image we need to set some settings within the django project now the two settings that we need to do are the media directory and the media root settings so let's go to our settings.pi file and underneath the static the static variables we're going to create a media route which we're going to set to the base directory and a media directory within that and i've already created this as you can see here within my project and currently it's empty but that's going to be our media directory and the media url is going to be slash media okay so you need to set these two variables in settings.pi and there is one other step you need to do as well we need to change our projects urls.pi which you can find within the urls.pi file here we're going to add the ability for django to to return our media files to us and let's see how to do this we're going to go to the browser here this is the django documentation and i'm going to share this link in the description to serve static files we need to use this thing here and append that to the url patterns so i'm going to copy and paste the two imports into our urls.pi file and i'm also going to copy this statement here which adds to the url patterns so with that we can now access the media files within our project the next step is to change the model to allow an image to be associated with it so let me close all these editors and i'm going to go to the models.pi and what we're going to do is to the film model we're going to add an image field so we'll call it photo and it's going to be a models dot image field and this takes a an argument called upload 2 and we can upload it to any directory within the media directory let's call it film photos and what that's going to do is it's going to upload all of the photos all of the images to the media directory which you can see here and it's going to create a film photos directory within that and upload all of these images to there we're going to see that in a minute but let's now go to the next step which is to ensure that we've got the pillow python imaging library installed in our project so what we're going to do is we're going to look at if we run the command pip list and you can do this in your virtual environment or if you're using a tool like poetry you can run equivalent commands i have the package pillow installed you need this to work with images in django it's a requirement so make sure you've got a pillow installed if you don't you can run the command pip install pillow like that i've already got that so i'm not going to need to do that let's run the server again and what i'm going to do now is um actually before i run the server we need to make the migrations and migrate these changes before we make the migrations here it's asking for a default value which reminds me that i've forgotten to specify that this should be nullable because by default um films do not have a an image associated with them so null equals true and we'll add blank equals true as well and then we can make the migrations and we should be able to migrate these changes after that so after we've done that we now have a film model that has a photo which is an image field attached to it in the next step we're going to change our film detail to show the image if it exists so let's go to templates partials filmdetail.html we're going to add a second div here and within this div we're going to check whether or not the film has a photo so it's going to be a django template if statement and it's going to be fuserfilm.film.photo so what this is doing if we go to models we have the user film and the template that you can see here that's returned by the view it's going to check to see if the film associated with this user film if it has a photo if it's not null okay so if it's if it's not now we can do something else we can do something else and what we're going to do if it's null is we're going to just render a paragraph tag that says no photo with a sad face and we can close the f block here with an end if if the user does have a photo or rather if the if the film does have a photo we're going to use an image tag to show it here okay so we check to see if the user film if the film instance that we're interested in has a photo and if it does we render its url and pass that to the source of an image tag and we have some styles here to make sure that the image doesn't go out of control so below that we're now going to show a very basic form and what this form is going to do is give the user the ability to upload an image that will be associated with the film so within the form i'm going to paste some code we've got a csrf token and we've got an input field of type file that will allow us to pass the file to the server for hdmx to work with file uploads we need to add these attributes to the form element first of all hx encoding and that's going to be set to multi-part form data now this is just something you need to do in html when you're uploading a form that contains files you need to set the encoding type to multi-part form data so we've got that and then we're going to what we're going to do is post this to a django url which is going to be called upload photo and again we're going to pass the user film's primary key to this url and we also need an hx target here to specify how the response data is going to be swapped in when we upload a photo so when we submit this form containing the image we need to specify that it's going to be swapped into the film list we're going to see what html is going to be returned in a minute finally at the bottom of this form i'm going to add a quick button which will allow the user to submit the form now we need to add the view and the url to deal with this so first of all let's add a url called upload photo so go to urls.pi and this is the third url of this tutorial upload photo and it accepts the user film instances primary key and finally we need to create a view called upload photo so if we go to views.pi and then to the bottom here we're going to create an upload photo and what we're going to do is we're going to use the same get object or 404 here to get the instance here now we can view what files have been uploaded in django using the request.files dictionary um if i return an http response here of an empty string we're just going to see how this works so let's run the server and we can see what this looks like on the front end so if i go to the films list we click fargo you see we get our sad face saying no photo here because fargo has no photo attached to it and we can go back to the list page here or we can actually try and upload a file here and if we try and upload and film here let's say the fargo film and we click upload file we see we get nothing back because we're returning an empty response but you see that the request.files dictionary is populated with this um this file here so we now need to save this to the model's photo field so let's try and do that now within viewers.pi we're going to edit this code and we're going to get this out of request.files now it's keyed by photo and that's because um within here we have a name of photo as attached to the file input field so what we're going to do is we're going to get that out and it's going to be photo equals request dot files dot get and we're going to get the photo and that will give us back the actual image object and then what we can do is we can use the user film that we pulled out of the database we can follow the forum key to its films photo and then we can actually call a save method the save method is available in django's image fields so we can call save and we pass the name which is going to be the photo.name that's the first argument and the second argument is going to be the actual photo itself and that'll save it to that image field and then finally we can render a context user film and that's going to be equal to user film and what we're going to do is we're going to return the same detail partial with the new image attached to it so once we've saved the photo to that particular user film we will return the partial with the film detail and that will come back here and if the film didn't have a photo before um you would have got this no photo now we have attached the photo so when we re-render this partial we should now see the image with that source so let's see if that works now if i go to the films list and we go to fargo and if we try and upload the file here what we're going to see is hopefully we're going to see the film image appearing here unfortunately we're not getting that here so let's try and figure out what's going wrong we've got the photo url here you can see it's not finding that within the media url so if we check the settings or rather the urls.pi i think the problem is i've actually set this to the static url let's try the media url and also the media root here let's see if that does the trick for us and if we refresh the page and go back to fargo now we actually see the image and that's the the thing we were looking for in the first place here so now we can upload a file and i could upload um another file if i wanted to by say the godfather here and that would change the file out to the godfather so we can do that now and we can also go back to our list component and if we wanted to upload the godfather's film we could go here and upload the godfather when we click upload file you're going to find that this no photo is replaced because we're returning the same template but it actually has an image now so when we click upload file we get the same thing back but with the image attached so that shows you how to upload files using htmx this is done without any page refreshes the most important takeaway from that is that within the uploader you need to set the hx encoding this is an important attribute to set when you're uploading any files in hdmx so thank you for watching this video if you've enjoyed it please like and subscribe we're going to be back with more htmx videos soon thanks for watching and see you in the next video
Info
Channel: BugBytes
Views: 397
Rating: undefined out of 5
Keywords:
Id: flqSOdo51tI
Channel Id: undefined
Length: 17min 59sec (1079 seconds)
Published: Thu Nov 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.