File Uploads | Python Django - The Practical Guide

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
we learned a lot about forums and submitting various kinds of data now one specific kind of data is missing though and that would be files this course section is about file uploads and file storage and we're going to learn how we can Implement file upload how we can then store these uploaded files somewhere in our project and how we can of course also serve them for that we're going to explore how file uploads work in Django how the data is being stored we're going to add file uploads and we're going to explore different ways of adding this and of course we're going to make sure that these uploaded files then also can be served to our users because often uploaded files are not just for us as the owner of this website as the developer but we want to serve them back to users for example in the case of user profile images where of course we don't just want to store on our servers and view them ourselves but where instead other users should be able to see the profile pictures of yet our users so we're going to dig into all these aspects step by step throughout this course section now to get started I created some starting resources for you I created a new profiles app and you can do the same by running python manage py start app profiles make sure you create such an app and then you can download my attached folder and simply paste it into your profiles folder so that you get the static the templates folders the views and the URLs py files which I pre-configured for you you also need to go to your project folder and to settings py there and add profiles as an installed app otherwise the templates won't be picked up for example once you did all of that you can run your development server again and with it up and running you can visit localhost 8000 slash profiles and you will see this basic form which has no content other than a file picker and a upload button and that's now the content we're going to work with throughout this section and we're going to learn step by step how file upload works and how we can ensure that it works in the way we want it to work so let's get started with file uploads for this I'll go to this template the create profile HTML file and here we get this basic form with the input of type file which is a default HTML input and this upload button which submits the form now to handle this submission let's go to views py there I created a profile view a create profile view view extending the view class and then I add my own get and post method now we learned about all those fancy classes the fancy class based views like the Forum View and the create View and we could look into those views but I want to get there step by step I want to really explain how file upload works because in your specific scenario you might need to handle it on your own in great detail and then you don't gain much if I show you one single way where Django does everything for you so we'll get there step by step throughout this module so that you understand all the details and therefore I want to work on this post method because that's a method which will be triggered when we submit that form at least once we configure the form appropriately to send a post request to this URL to this URL which we actually load under slash profiles so let's make sure that everything is configured appropriately on my form I'll set a action attribute and point at slash profiles so that a request is sent to our domain slash profiles and the method of that request should be post so that it's a post request now as soon as you have a forum with a file input inside of it as we have it here you should add a special Inc type attribute as well which is another standard HTML attribute you can add to forms and the value here should be multi-part slash foreign data this tells the browser that this forum when submitted will contain a file and that will simply change how the request is sent to the server and you need to add this to ensure that a properly configured request is sent to the server specifically it will be a post request sent to slash profiles no as just explained that will reach this View and there it is post method so in there we now want to handle the uploaded file and as so often Django has got us covered this request object as you might recall has a post property which gives us access to all the submitted Forum data now that is only non-file data but Django also gives us access to any submitted files on the request files property another special property populated by Django which gives us access to uploaded files now in our form we should give our input here this file input a name and I'll name this image because let's say we want to upload an image here and we could add more attributes here by the way to configure this file there also are HTML attributes which can restrict which kind of file can be chosen by the user but that's not something I want to do here instead I'll just give this a name of image and then back in the view I can now use this name image here as a key on this files dictionary to get access to this specific file which was submitted as part of the form and if we had multiple file inputs in one in the same form we could retrieve the different files by the different names that were assigned here so now with that we get access to that file and for the moment let's just print that file to get an idea about what we actually have here now of course I then want to do something once we handled that file even though handle is a is a big word at the moment we're just printing it but I want to do something thereafter I want to redirect so I'll import from Django HTTP I'll import the HTTP response redirect and then indeed return HTTP response redirect and I want to redirect back to slash profiles so back to this um Forum in the end I will reload the form but by sending a redirect instead of just rendering the template again with all that in place let's save all the files let's go to this form and let's choose a file here and I will choose an image of me which I prepared you can of course pick any file you want and I'll click upload and now I get an error you call this URL via post but the URL doesn't end in a slash and you have a pen slash set now that's just a a little oversight from my side we should point at slash profiles slash here as an action then reload our form choose that file again and upload it again and now we get a csrf verification failed error which makes sense because I explained that for security reasons forums which are sent with a post request need to contain this csrf token so in this form we should again use this csrf token tag here which Django gives us and includes that inside of the form now with that if we reload this one more time and pick this image if I click upload it looks like it worked at least we get no error if we go back here we also see me dot jpeg in the terminal and that is coming from our print statement that is this uploaded file now it's just printing the file name here but actually what we get here is not just a name it's a complete object which we can work with if you search for Django uploaded file you will find the official file upload documentation but you also find this file uploads reference and here you can learn about this uploaded file object which we got now and which methods and properties you can access there like the name the size the content type of the file and you can also read it or read it in chunks to move or stream it to a different part of your project folder because at the moment of course it's uploaded but you don't see that file anywhere in your project folder now in reality you might want to store such a file of course and that's there for what we're going to do next so we got hold of the uploaded file but now we also want to store it and for this I'll add a little helper function Here in My Views file we could create it in a different file as well but I want to keep it here so that we see all the related code on one screen and I'll name it store file and I expect to get the file as a parameter here the idea is that this function should now be responsible for storing the file somewhere on our system and we can do this with a pretty standard way which we have in Python when it comes to working with files we can use the with keyword and then call open which is a built-in function in Python to open a file and then open a file in a directory of our choice and here I'll name this temp slash and then also include the file name the uploaded file should have and I'll just go with image dot jpeg here and of course I have a bunch of assumptions here this will for example only work for JPEG files so if you are uploading a PNG or a PDF please adjust this appropriately of course well later learn how we can make this more flexible now I'll open this file and if it doesn't exist yet I want to create it so I'll set the mode here for opening this to WB plus which is prepared to work with binary files and will have such an incoming binary file here and it will write that file to that location in the end now I'll save this under a variable named test for Destination and now that's the destination of the file where it should be stored now we can create a for Loop in here and go through all the chunks in file dot chunks calling that chunks method which this uploaded file class has and this simply will read the incoming file in chunks instead of reading it entirely in one go we could use read instead to read it in one go but as you see here in the official docs if that's a large file you would occupy a lot of memory with just this file so reading it in chunks is more efficient that's why we Loop through all the file chunks and then chunk by Chunk we will write it to this destination to do so we simply use our test and call right to write this chunk to that destination so that we create this destination file step by step from the chunks of the file we receive as a parameter here and now instead of printing the uploaded file I will call store file and pass this uploaded file to store file to store it there now let's see if that works if we save this and go to our form if I pick that same image again and click upload I get an error that this directory temp does not exist so we need to create such a folder and we don't need to create it here in the profiles app but instead this code will basically run in this overall project folder so therefore we should add a temp folder here in the main project folder and with that temp folder created here in our overall project folder if we now go back to slash profiles and we pick a image and we click upload we don't get an error anymore and if we have a look at this create a temp folder there now is the image JPEG file which is this uploaded image so this now works and this is how we can generally handle file uploads but of course this approach has a couple of flaws it's fairly static it only works with JPEG files and we have quite a lot of custom logic which is very flawed and very much not flexible therefore whilst this is an approach we could use and which we of course could make more flexible by adding more code let's instead now dive into the various helper functionalities Django has for us when it comes to managing file uploads now when it comes to simplifying file uploads it is worth noting that in the end we still just have a form here this file input is still just a forum input so why don't we handle it with the Forum tools we learned about earlier in the course and they offer I will add a new forms py file here to create a forum which will be based on Django's form functionality which then hopefully simplifies this whole file upload thing for this informs py we can import from Django the forms object the forms module and create our profile form which inherits from forms.fore I'm not using a model form here but a regular form because I have no model at the moment then here we could of course add all the form fields we might need of course you are allowed to mix non-file and file data in one of the same form but here I only have my file so I will only add one field here let's say the user underscore image where I will use forums.pile field and this does what the name implies it sets up a form control for accepting files now we can configure this and as always you can dive in the official docs for all the options for example you can configure whether you want to allow an empty file but here I'll just set it up like this and then in views py in the get method I want to create such a form so from dot forms from my forms py file I'll import that profile form here and in the get method I'll create that form by instantiating profile form and we pass this as context to the template so the form here is that created form and then in the template itself we can replace all the inputs with form as you learned it or of course render it in Greater detail with full control over everything and over all the labels and controls and errors as you learned it I'll not recreate all of that I really just want to focus on the uploading part so I will let Django create the entire HTML form for me here and with that if we save all files and reload it looks a bit different but we now get this Auto created forum but this doesn't help us yet we're still handling the upload logic manual here in post now at least we can also create a profile form there though to pass in the submitted data and also the submitted files that's then our submitted form and we can then still check if submitted form is valid and this will then validate all the form data including the files which for example checks that a file was added and then here if we are valid I'll store the file else if we don't make it into this if block I'll return my create profile template again and set my submitted form here as a value and now we can at least add some basic validation without having to write Logic for this ourselves so now if I try to submit an empty form I get this error that's a browser warning though if I cheat again and I simply remove required from here from that input I can submit this in the browser but now I get this error from Django so that works now we have validation which is at least a little bit of help but it's no help when it comes to storing the file and for this to simplify that we want to combine a form with a model because it turns out that of course most files which are being uploaded are related to some model for example the image of a user is related to a user model and therefore Django has a rich support for managing uploaded files with help of models so let's go to models py now and in there I'll add a new model and I'll name it user profile it's not really a user profile because it only will have a file image but of course as I said before there could be more data involved now as before here we'll extend models.model and now we set up all the fields we want to have like a first name and so on but here it's only about the image so I'll add a field named image here and this will now be a special kind of field not a Char field containing some image name or anything like that but instead a file field there also is an image field and I'll come back to the second but let's start with the file field the file field wants a file but the interesting thing now is that this file will not will not be stored in a database because it is considered a bad practice to store files in the database it loads the database makes it slow and the database simply is no file storage instead files should be stored on hard drives therefore what file field will do under the hood once we save a model with a file is it will take that file and move it somewhere on our hard drive on our disk and Only Store the path to that file in the model in the database so that just the path is stored in the database but if we then need to use that data and display that image for example it knows where to find the file that's pretty smart and it also handles moving that file to that location for us so that we don't manually need to move the file anywhere to achieve this we need to add the upload to parameter and tell Django where to store this file and now we don't want to construct a long path which would have to be absolute on our entire file system so we can't just say data here and Django would use a data folder next to models that's not how it would work instead that would look for a data folder on the root level of our operating system so totally outside of this Django project and that's not what I want therefore we'll go to the feedback project folder and dive into the settings again and here at the very bottom we can add a new setting which doesn't exist yet but which we can add now and that's the media route media root will tell Django where our files should be stored in general and then any folders we might point at in our models will be subfolder to that media root this should be an absolute path and I want to pick a folder inside my project here in this case so I'll add a new uploads folder here on my root project level and I want to use that folder here now to construct this path I'll use the base stir which is a variable available here in the settings file which points at this overall project folder so this overall folder and then simply add slash uploads and this now constructs an absolute path to this uploads folder now this media root setting will automatically be taken into account when a file is uploaded and moved because of a file field so now here I want to move the file to a data folder or maybe to an images folder let's say inside of that uploads folder now we have this profile and we could add more Fields but now I want to use this profile or this model this user profile model in my view so for this we imported here from dot models we import user profile and then here in post if we have a valid form instead of storing a file like this what we can do now is we can create our profile we can instantiate our user profile model and then set this image field just as you learned it by adding the image keyword argument and here we can now point at the file so like this and actually here it's user image as an identifier now because our form is now rendered with help of the form class and that class uses user image as a field name for the file field so that is then the key by which we can retrieve the file so hence here I access user image on the incoming files and I store that file as a value in the image field of my user profile now behind the scenes Django will do what I explained it will take the file move it to some other location on our file system in this case to the uploads folder and Only Store a path in the database but we don't need to worry about that we just need to call Save here and Django will do all the rest and now with that if we save this now we don't need to manually write the file anywhere we can delete the store file function here instead now Django will take care about that so with all files saved if we visit slash profiles again and I choose a file here and upload this I get an error no such table because of course running migrations first would be an idea since we added a model so let's quit the development server and let's run make migrations and once that happened we want to run migrate to also run them and once that is done we can run our development server again now we got the database initialized so now if I reload the profiles page pick the file again and upload it that's looking good no error here no error here and in uploads we now have the images folder and there we have that file and you see it was added twice because I tried it twice just saving it to the database then failed because of the missing migrations and you see that different names were assigned automatically by Django since we had a name Clash since the same file was uploaded multiple times and we don't need to worry about those different names because a path to that file was stored in the database and we can see this if I open a new terminal tab here if I quickly fire up that shell and I then quickly import from profiles.models I'll import the user profile then if I use user profile objects all I have one object in there and if I access this through its index and I then access the user image field I get an error because it's just image not user image so if I access the image field then you see it is able to make that connection to that file and here we can then access things like the path to get the full path to that file where it was stored we can also get the size of that file so that is looking good now and with that we now utilize a model to let Django handle the storage of our uploaded file now let's see what else we can do with that so we're using a model to deal with uploaded files and it's working just fine but here I'm using a file field even though I'm actually interested in only images and this again is a quite common scenario that you deal with images being uploaded that's why Django also has an image field the idea is the same you still get files being uploaded and you move them to a folder of your choice so you still specify upload too but now Django will only accept images and no PDF documents for example which of course can be an improvement and simply ensures that you don't have to validate the file type on your own however if we switch to an image field and we save this we get an error that we're basically missing some extra package which you need to install and it also shows us how to install it because for dealing with images specifically and for analyzing images and files for being an image and so on we need to install that extra package so I'll quit that development server and run python3-amp pip install pillow to install this extra package which Python and Django needs to successfully work with image only files and once that's done we can of course restart our development server and now that works now in our form I also will switch from a file field here to an image field and if we now save this as well and we go back to slash profiles now if we inspect this file input here we see that it for example only accepts images here in the browser already so that we have this first in browser validation but it will then also validate this on the server so if I pick an image and I upload this this still works and this still is stored but if I would have picked a different file like a Word document or a PDF document it would not have worked now since we have a model and a form we can of course also create our form based on a Model form as we learned it but even more than that if we don't need any specific configuration regarding the labels or the error messages we can actually even skip that part and go back to our view and replace this create profile view with a new view based on one of these more specialized views we had a look at in the last course section for this from Django views generic edit we can import the create view for example and then recreate our create profile view based on that create View and then there we don't need to add get then post and so on instead we just inform Django about our template so that this is the template in profiles create underscore profile HTML and about our model which in this case is the user profile then we also add fields and tell Django that all the model Fields should be rendered in the template in this case it's only the file field anyways and we're basically good we just need the success URL and here I want to go back to slash profiles so that's this redirect URL and that's all now validation and also saving the data including files will be taken care of by Django so now we can get rid of this long old class here and instead just use this and with that if we save this we still get this form and we still can pick a file here upload it and it works it's still getting stored as you can tell here by this new file which was added the green one here so that's of course very convenient and now also shows you again why these specialized views can be interesting now we don't have to worry about the nitty-gritty details we can delete this form class all we need now is our model configured as we need it and we're good to go and that's how we can handle forms with files and file upload but one important thing is missing and that would be serving these files that's something we're not able to do yet so let's now see how we can serve uploaded files let me create a new template for this and I'll name it user profiles.html name is up to you and I'll create a blank skeleton here with a title of user profiles and again I won't add any specific styling here because it's really just about the functionality here not about making this as pretty as possible now here my goal is to Output a unordered list of all the user profiles which in this case means a list of all the images since a user profile in our case is just an image so now we need a view that fetches all those user profiles and exposes them to that template and for this of course we can create a new class here in our views py file and name this profiles View and here we could paste this on the base View and write everything on our own or again import from Django views generic and import the list view there then I inherit the list view or from the list view here because of course I want to fetch all the entries for a given model hence we add the model key here and point at the user profile model and I'll Define the template which in my case is in the profiles folder the user underscore profiles HTML file so that's the template which should be rendered for the fetched user profile data I also want to make sure that my context object name is profiles instead of object list which would be the default and we're almost good to go we just need to add a URL now and here I'll add a path where I say list and then I want to Target my profiles view as a view here and render this view for slash profiles slash list and that will then fetch me all the profiles and expose them under the name profiles in the template so back in the template here in the unordered list we can Loop through all the profile in profiles that's that key name I just specified in the view class and end the four block here and then in the list items I'll just add a image tag where I want to render these images and now the question is what should we add here if it would be a static file we could use static and then a path at the file now it's kind of static it is an image it's not a template it's not python code but it's also not one of our predefined static files now thankfully we don't need to make it that complex all those files managed by our models through the file field or image field will have a specific property we can drill into on our model field so here profile will of course refer to an instance of our user profile model and therefore there we have the image field so we have profile.image here now on that image we can drill into things like the path or also the URL the path would be wrong here because that's a file system path which is nice to have if we run some python code on our server but here if we want to define a URL that should be loaded by the browser keep in mind we're defining the source for image tag so this has to be something the browser can reach then we don't want a path on the file system on the server which is not accessible by the browser it shouldn't be for security reasons but we want to have a URL which points at this file specifically and that's why Django gives us a URL field on the files stored through a file field or image field in our models so therefore that's what we can add in our template to get hold of that specific uploaded file so if we now save all our files again and we visit profiles slash list we get a bunch of images there but clearly not our uploaded images instead some placeholders now it does have a URL here but it fails to fetch them fails to load resource the server responded with a status of 404. so something is missing here we're not able to serve those files now why is Django not able to find our images why does this URL not work because by default Django locks down all your folders and does not expose them to your browser for security reasons so all those folders are locked they are not accessible from outside the server the static files like CSS Faults Are exposed by Django but these are exceptions in general nothing is accessible I also wouldn't be able to try to access any of the Python files here through the browser but because of that our uploaded files are also not accessible by default now to make that work we should do two things first let's go to our main project folder and there are two settings and then at the bottom besides adding media root also add a media URL and then add two slashes and in between any path of your choice like for example user underscore Media or user Dash media now that's up to you but that will be part of the URL which will be exposed to the outside world for accessing your uploaded files because we won't navigate through the actual file system here instead we'll construct a URL that leads to those uploaded files and here you can Define how that URL should start basically how that should look like Django automatically does something similar for static files these files like our CSS files are served from our domain slash static slash and then the path to our aesthetic file now we're going to do something similar for user uploaded files so this is Step number one but that's not all because this alone doesn't expose anything instead we should go to the URLs py file and here I'm going to the one in the app folder actually we need the global the main URLs py file in our entire project I'll move the code over there in just a second I started editing the wrong one here but we need to edit the URLs py file the main project URLs py file to be precise and here on the URL patterns you should add something with a plaster after and call a static function which is provided by Django for this from django.com.urls.static you should import static that's a helper function which serves static files as I said Django is automatically configured to serve CSS files in development and static images you might have in your static folder but for data stored in other files like our user uploads here we have to manually make Django aware of those folders which should be exposed to the outside world now static ones two arguments it wants to know the URL which should be used for exposing the files and then the path on the file system that holds the actual files now for the URL we want to use that media URL we exposed in our setting or be configured in our settings and we can get convenient access to that by importing django.cons and from there importing the settings and then we can add settings Dot media URL we can access this here to get access to that media URL we defined and then the other argument we should Define here is the document underscore root argument this now wants a path to the folder that contains the files that should be exposed and that's our settings dot media root because just to make this clear media root is the path on the file system where the files are physically stored media URL then is the URL we want to show to the outside world from which these files can be loaded and the mapping between URL and the actual path on our file system is done by this static function which we're calling here and with that we're exposing all the files stored in our media root so in our uploads folder through that media URL to the outside world and Django will automatically pick up the media URL and add it to that URL we can access on every image so with all those changes made I'm almost there but I just realized I did this in the wrong URLs py file you should not do this in the app specific URLs py file instead cut it from there and go to your main project URLs py file and add plus static and so on there and then also add the Imports there so the imports from Django cons and djangoconf URLs static remove them from the app specific URLs py file and instead add them to your project level URLs py file because it's this main entry point for all the requests where you need to enable this static serving of your user uploaded data and with that made with that project level urlspy file edited if I now reload profiles list those images are being displayed here so now that is working and now we could of course tweak that template however we want and this is now how we can serve uploaded files it takes a couple of steps for security reasons but in the end it's just about tweaking that URLs py file about setting these settings here and about then accessing the URL on the uploaded file in the template as we're doing it here now of course we're also going to see that again in action once we apply what we learned to our blog project now this is it for this module we learned how we can upload files and we started with a manual approach but then quickly moved on to a model driven approach where we add an image or a file field for the image field we needed to install that extra pillow package and where we then conveniently told Django where to move those uploaded files and you learned that in the database it's not the file which will be stored but just a pointer just a path to that file now for that to work smoothly we needed to go to settings py and set the media route where we Define our folder where those uploaded files should be stored we also defined the media URL which we then needed for serving those files we also needed to tweak our root URLs py file to expose those uploaded files statically because by default none of our folders in this project are accessible from the outside world for security reasons that's why we need to explicitly open up specific folders like the upload folder here in the end if we want to Grant access to them and with that we then were able to also list our user profiles and display those uploaded images and that's in the end all that's to it file upload can sound very complex and we definitely had an in-depth look at it in this course section but ultimately it's just like handling a regular form and the only tricky part can be serving those files but that's something we also covered in this module now
Info
Channel: Learn With Udemy Course
Views: 462
Rating: undefined out of 5
Keywords:
Id: ECy9Sez4C7I
Channel Id: undefined
Length: 47min 9sec (2829 seconds)
Published: Sat Jul 22 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.