File Uploads - with Django REST Framework!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to learn how to handle file uploads using D Jango rest framework and we're going to show how to integrate these file uploads with jango's models and with the database and we'll learn about Parcels in Jango R framework that look at the content type HTTP header and use that to decide how to correctly parse incoming request content and we'll also see how to use things like readon fields in jangle rest framework and finally we'll also write some JavaScript code that calls a rest framework API and uploads a file over an agent request and that functionality mimics how you might call a file upload endpoint using a framework like react or VJs or a similar single page application framework so let's get started if you're enjoying this content give it a thumbs up and subscribe to the channel and consider buying the channel a coffee if you're finding this content useful let's dive in so we're going to start by creating a jangle model that has a file field and let's say we were creating a job application website and we want users to upload their CVS or their resumés and have that uploaded file stored on the file system with a link to that in the database table now what we have here is a models.py file and we have a user model that's extending D jango's abstract user and if we go over to settings.py and go to the bottom of this file you can see we've set the off user model to point to that particular model so let's go back to models. Pi and we're going to add some new fields to the user model here so we're extending what jangle gives us with some extra Fields now at this point there's two things we can do if we want the user to have a CV on the system first of all we could take the approach where a user can only have a single CV on the system at one time in which case all we need to do is create a file field here on the user model and that's because one user can only have one CV at a given time now if you want to allow a user to upload multiple CVS you could then create an entity in the database and Link the user to that entity and it's going to allow the user to upload multiple CVS to the system and they can have a Time stamp and the most recent for example could be selected by default but a user could choose between different CVS we're not going to take that approach here let's just keep things simple but it's worth mentioning that if you need multiple CVS you can use that approach so I'm going to create a field here on the user and it's going to be called CV and that's going to be equal to ajango fail field now when we upload the object we're going to store it in a particular location under the jangle media directory and we can specify that location with the upload to param and I'm going to store this in a location called CVS and we're going to set it to be nullable by setting null equals true and also blank equals true and this means that a user can be created in the database even if they don't have a CV on the system and as well as that I'm going to add a second field here called uploaded at and that's going to be equal to a models. DAT time field and again we're going to allow this to be nullable so let's pass those same two parameters now once we have the amended user model let's go to the terminal and we're going to run the make migrations command and then after we've done that we can run migrate and when we run the migrate command it's going to make those changes to the database now because we're uploading files here what we're going to also do is go back to settings.py and at the bottom here I'm going to set a couple of settings the first one is going to be the media route we're going to look at the base directory and create a media directory underneath that and that is where all user uploaded media files are going to be stored on the local file system and as well as that we're going to set a media URL here and I'm going to set that to/ media so we now have the model and database set up for storing a file against the user what we're now going to do is bring in jangle rest framework now to begin with we need to install rest framework so at the bottom pip install and it's Jango rest framework all one word we can install it using that command and make sure you do that in a python virtual environment and what we're going to do next is we're going to create a new file here and it's going to be a serializer Jango rest framework that we're going to create and let's say that we have a user on the system and we're going to allow that user to upload a CV so let's create a new file here in the core application called serializers.py and at the top of this file I'm going to import a couple of things first of all the serializer module from jangle rest framework and also the user model that we've created now let's create a class here this is going to be the serializer class and it's going to be called CV upload serializer and we're going to set this to a serializer iers do model serializer now you could also use a serializer here but we're going to use a model serializer and we're going to link it to the user model and The serializer Meta class so the model is going to be user and we only want to have a single field here so we're going to pass fields and set that equal to a list containing a single element and it's not resume it's CV now this CV field exists on the model it's this field here that allows the user to upload a file to the file system so what we're telling Jango res framework is that this particular serializer should operate only on that field on the user model here once we've created the serializer what we can do is go to the Jango views.py file and we're going to create a jangle rest framework class in this file so let's create a class just below here and this is going to be called upload CV View and I want that to inherit from the jangle rest framework API View and an API view in rest framework this allows you to perform or to Define methods that match the request type so for example a post request you can define a post method here that takes self and the request in and you can perform any logic you like within the post method when that post request is received by this view here so we need to import the API view from Jango rest framework and I'm going to go to the top here and bring quite a few import Imports in here at the top so I'm going to paste these in here from rest framework we're importing status we're also importing the response object in rest framework and of course the APM I view class as well this is a base class that you can inherit from when you define some views and rest framework it's one of many ways to Define views and rest framework and it's the one that we're going to use for this file upload view now one thing we can do with the API view is we can define a property on that to tell the API view what the expected serializer class will be and that is the serializer Class Property we're going to set that to the CV upload serializer and again we need to go to the top here and import that from serializers.py so we've imported the CV upload serializer what do we want to do in the post request method when a post request is sent with the user's CV now I'm going to add a comment here we want to assume that the request. user will be sent to an authenticated user but I'm not going to handle that in this video we're not going to build logins in this video let's just keep it simple and what I'm going to do is a small ha gear I'm going to set the user property on the request and we're going to set it to user doobs first so what that's going to do is pull out the very first user from the database and it's going to set the request. user property to that user I'm going to go to the top here and import the user model from models.py and I want to stress here again anywhere that we use request. user in this method that we're about to Define that would normally come from the authenticated user but in this case we're just pulling that user out of the database for Simplicity so then I'm going to get a reference here to the serializer by calling self. serializer class and that's a method on the API view in jangle res framework so if you've defined a serializer class property on the API view you can get access to that with this method and then we can pass an instance into this so we're going to pass request. user and remember in a normal view that will be the authenticated user and as well as an instance we can pass some data in here and we're going to pass request. dat now what request. data is in jangle rest framework is it's a reference to the content that's been sent in this case the post request so when the user posts data in Jango rest framework we can get access to that with request. dat now this do data property is added to the request object in jangle rest framework is not available in a normal jangle request so if you're accessing request. dat make sure you're doing that within a subass of ajango rest framework view otherwise if you're wanting to access the posted data in a normal jangle request object you can get that through the request. poost query dictionary now just to explain this line of code here I'm going to go to the rest framework documentation on serializer you can see we have a class here called comment and it contains some properties such as email content and created and then we create a comment here and store it in a variable now we can create a serializer around that data and this one's called comment serializer and then you can serialize objects by passing them into the serializer so that's one way of creating a serializer when we pass an object into it but I'm going to scroll down a little bit here to this section on saving instances if we go down a little bit further here we can see this section here now I want to highlight this when we call do save this is either going to create a new instance or it's going to update an existing one and this depends on if an existing instance was passed in when you instantiated the serializer class so let's look at these two definitions here this is going to create a new instance because we're passing raw data into the serializer so it's going to create a new instance of the model on the other hand if we look at the line below we pass an instance in along with the raw data and what that means is it's going to take that raw data and update the existing comment instance now if we look at our code back in views.py we are instantiating the serializer class and we're passing the user model in here along with the raw data from the request and this means that the user model is going to be updated we're not going to get back a new instance of the user model it's just going to update the existing model and once we've created that serializer what we can do is check if it's valid with the serializer do is valid method and notice that the API for this is exactly the same as it is for D Jango form classes we can then call serializer Dove and again as I said a second ago that's going to update the existing user instance with the new data and the new data coming in with this upload serializer is just going to contain the file field for the CV so let's get back to views.py and after we've saved the serializer that's going to update the database table for the user and we can now now return a jangle rest framework response along with the serialized data now after we call serializer do is valid we can get back the data from the serializer using the serializer dod dat property and finally if the serializer is not valid we're going to go down here and return a response with the serializer do errors added to that response and that's going to have a status of HTTP 400 bad request so I hope that makes sense we've got an upload CV view here and that's a subass of the rest framework API View and that takes a post request and it's going to serialize an uploaded file that's coming in from the request. data and it's going to use the CV upload serializer to do that and that particular serializer has a reference to the models CV field and that's a file field once we've got that data coming in and we have it in the serializer we're going to check if everything is okay and then we save that data to the database and return a response to the client let's now go to urls.py and we're going to create a URL for this jangle R framework view so let's add a path View and I'm going to give it the rout of upload dcv and then we're going to link that to the view that we created and that was views. upload CV View and because that's a class-based view we need to use the as view function in jangle to convert that to a functional view that will then be used in the jangle application and then finally we can add a name for this URL and it's going to be again upload dcv okay so we now have a way to upload a CV in this project we're now going to test this out so let's save this and at the bottom I'm going to start the Jango server and in fact before we do that let's create a super user here we need to have a user in the database because we're pulling out a user using this user. objects. first method so I'm going to create a user with the name of admin and we'll give the user a password and that is going to allow us to then log into jango's admin UI and once we've done that we're going to go to admin.py and we're going to register the user model so from Models let's import user and then we can use admin. site. register in order to register that user model so that we can access that in the jangle admin UI so let's now run the server and we're going to go to the browser so I'm now in the Django admin UI we're going to go to the user model and we can see this admin user that we have here if we click on that user and scroll down to the bottom you can see the CV field here at the bottom and that's currently empty it's currently not got a file attached to it so the next stage is to use the API that we've created in Django rest framework with this particular view that we created here in order to upload that file so I'm going to go to the API view provided by jangle rest framework and that's actually at/ upload CV that was the URL that we configured in the urls.py file now we can see we're getting this error and the reason for this if you get this error it's because you've not added rest framework to installed apps so let's go back to J goes settings.py module and we're going to go to the installed apps setting and we're going to add restore framework to that setting once we've done that we can go back here and refresh the page and this time we get presented with this page where we can upload a CV now as it says here a get request is not allowed but you can see the HTML form below we can choose a file here and then we can post that file to the back end so let's try that out so I've selected a file here called CV sample this is a dummy file it's not actually a CV and what we're going to do is just post that to the server and you can see what we get back here is a single field called CV and we have a link to the actual uploaded file now you can see if we expand this a bit that the file is in the media directory and what we have is a URL here for the media URL for this particular resource so it's in the CV's directory and there is the name of the file that we uploaded now if we go back to vs code and look at the media directory we can see a new directory has been created there called CVS and we now have the uploaded file appearing in that directory this CVS directory this is being created here because of this upload to a parameter to the file field now let's go back to the rest framework API view here as you can see if we look at the raw data here the media type expected by default is application SL Json now when you upload a file you're not going to send Json data what you're going to send is typically multi-art form data and this is just a different encoding type it's a different way to send the data from the front end or the client to the Django server I'm going to go to the rest framework documentation and if you look at the API guide there is a page on parel now rest framework includes a number of built-in parcel classes and these allow you to accept requests with various media types and let's look at the section on how the parsel is determined here so the set of valid Parcels for a view is always defined as a list of classes we're going to do that in a second but when request. data is accessed what rest framework is going to do is it's going to look at this content type header on the incoming request and using that header using the value of that it's going to determine which passor to use in order to pass the request content so when you're developing client applications you should always remember to make sure that you set the content type parameter when you send data in an HTTP request and if you're using react or VJs and you're typically sending data to the server you're often going to be sending data in the format of Json so application Json is a sensible default for rest framework and it's also a sensible default to assume that an API might return Json data as well now you can specify a list of Partiers on a class and these can also be set globally in a rest framework setting called default paral classes and this setting here is setting that default to the Json partial now if we scroll down we can see that a set of partials can be set for an individual view or a view set in rest framework and all we need to do in the API view in this case is Define a field called parel classes we're going to do that now and we're going to set it to a set of partials that we expect to be used on this API view now if we scroll down again we can get the API reference now rest framework comes with some built-in poers we have the Json parure we have a form paror and what that's going to do is pass HTML form content and it populates request. data with a query dictionary of data now note this statement here in rest framework you typically want to use both a form passer and a multi-part passer together in order to fully support HTML form data and the multiart passer is below here and what this is going to do is pass multi-art HTML form content and importantly that supports file uploads so what we're going to do is take the advice of rest framework we're going to set the passor classes to the form passer and the multi-art passor and that's going to tell res framework what this view is going to expect in terms of incoming request data so let's go back to the views.py file in our application here and we're going to add a new property to the upload CV View and that's going to be called partial classes we're going to set that to a list and I'm going to go to the top of the file and from rest framework. parel we're going to import the two Parcels from that module so let's copy the names of those and reference them here in this posture classes field so we've added the multiart parure and we're also going to add the form poser and we can now save this after adding that new field let's go back to the API now and refresh this page as you can see by default the media type has now changed the expected media type is multi-art form data and that's because we've told rest framework what we're expecting here and that's HTML form data that includes a file so it's going to be encoded with the multi-art form data media type now if we try this out again after adding that new field let's go back to the HTML form and select that same CV sample once we've selected that we can hit post and again it's going to return the file URL for us and notice that because we've uploaded a file with the same name as the original file that we uploaded earlier it's added some random data to the end of that file name and that's just to avoid it overwriting the other file in the media root directory so let's now move on we might want to improve this set up in a few ways first of all if we look at models.py when we upload a CV we're going to want to update this uploaded at field in the model and set it to whatever the time stamp was when the CV was uploaded now currently nothing is happening with this field so what we're going to do is go back to serializers.py and as well as the CV I'm going to add a reference to that field called uploaded at now one thing to not is that this should be set automatically when the file is uploaded and we're not expecting this to be sent over the API from the client to the server so this is going to be what's called a readon field in jangle rest framework and in order to make it readon we're going to override the default definition for this field it's going to be a serializers DOD time field and what we can pass here is a keyword argument called readon and we're going to set that to true so by setting readon to true we're telling jangle rest framework and we're telling the serializer that we don't expect a field called uploaded dat to come in from the client but we do want to return that after we've saved the serializer and we have the new instance we want to return the uploaded that in response now in order to set this value what we're going to do is override a method on the serializer and that's the update method and that takes self and an instance as well as validated data now if you're wondering what this does I'm going to go to this website here for classy Jango rest framework I'll leave a link to this in the description of the video this gives you detailed descriptions with full methods and attributes for all of Jango rest Frameworks class-based views and serializers so we're going to go to the serializer class here and I'm going to SC scroll down to the methods section so we can look at all the methods that we can override on a serializer and this one here is the update method and you can see it takes self instance and validated data as parameters and we provided that here and if we go to the model form so I'm going to go back here and go to sorry the model serializer what we're going to do is go down again to that update method and we can look at the definition of this in a model serializer and you can actually see here the logic that's performed when the update method is called now now all we're going to do is go back to VSS code and we're going to take the instance and we're going to set it to the current Tim stamp so let's take that instance and we're going to set the uploaded at property and that exists on the instance because we have a model serializer so the instance is going to be a user and that user has this field called uploaded at and we're going to set this to time zone. now now we need to import the time zone module at the top here from Django do utils and as the name implies time zone. now it's just going to give you the current date time and then after we've set the uploaded that property we're going to call the update method on the super class so we're passing the instance into that as well as the validated data so the only thing we're doing in this overridden update method is setting the instance. uploaded that property to the current date time and then we defer to the super class which in this case is the model serializer update method and because we have read only set to True when we set this when the file is uploaded we get back a response and this time the response should include this Tim stamp when the file was uploaded so let's now test that out by going back to the rest framework browsable API what we're going to do here is upload that same file once again so I'm going to choose the file now and after I've chosen that we can post this to the back end and this time we not only get back the CV reference but we also get back a Tim stamp when this CV was uploaded and if I go back to the jangle admin and we look at this admin user if we scroll to the bottom bottom you can see that the CV is now referenced here and we now have an uploaded that Tim stamp as well now let's finish the video by showing how to call this API from a client side application so we want to upload a file essentially using JavaScript and this could be done in a react or a VJs application or a spelt application or similar now to keep things simple in this video we're going to write this code in a jangle template so I have a templates directory here with an index.html file and this file is what's returned by The View that we have in the jangle project so it's returning index.html with an empty context let's go back to index.html and we're going to add some HTML code here let's add a form element and what we want to set the action to here is the URL that we have that's defined here for our upload CV view so I'm going to copy the name of this let's go back to index. URL and we're going to use the jangle URL template and we're going to paste in the upload CV URL here so it's going to post the data to that URL and let's set the method to post here and because we're uploading a file we need to set the encoding type that's the ink type attribute on the form and we set that to multiart SL form data and then I'm going to close the form element and because we're sending a post request I'm going to add the Cs csrf token template tag inside this form and that's going to add a hidden field to the form that's going to allow you to submit this data once we've done that let's add an input of type file and we're going to give this input an a name of file as well and we can close that off and finally we need to allow the user to submit this so let's add a button of type submit and we'll give that the text of submit here and then we can close off this button so a very simple form here it's not going to look good on the browser but it's going to do for this video let's go to the browser and I'm going to go to the root URL of this application and we have the form here where we can choose a file from our file system and we have a submit button here now what I'm going to do is choose that same CV file now and now that that's chosen I'm going to submit this and you can see that we get redirected here to the jangle rest framework API response so the upload is working we've selected a file in that form but we're being redirected on the response to this upload CV page now in a single page application or even with HTM X we'd submit this form with the file field using an ax request that does not reload the page and does not redirect us to somewhere else in the application so what I'm going to do is go back to this page here and we're going to go back to the code for this and we're going to rework what we have here to call the back end via an Ajax request now in order to do that we're going to need to write some JavaScript here just for demonstration so let's create a script tag within this template and we're going to listen for the load event on the window and when we get that we know the page has loaded and we can start referencing elements on that page so I'm going to get a reference to the form and to do that we can use the document. query selector method and we can get the form element so document. query selector is a JavaScript Dom method and it's going to take a selector as a parameter and it's going to return the first one of these it finds and there's only one form here so it's going to find this and return it and that's going to be stored in the form variable so just a heads up here I know there's a lot of HDMX fans that don't like JavaScript but we are going to write some JavaScript for the rest of this video but hopefully that's going to demonstrate how we can call these faile API views over an Ajax request so once we have the form we're going to get a reference to the submit URL so the form has an attribute called action that contains a reference to the URL so we're going to get that now and I'm going to store it in a variable called submit URL and that's going to be equal to form. getet attribute and get attribute again that's a method in the Dom and we're going to get the action attribute out of that form and I want to get a reference to something else and that's the input of type file so let's paste this in here so again we're using the query selector method and we're looking for an input of type file and the final thing I want to do is get a reference to the Jango csrf token and to do that we can use the query selector method and again we're looking for an input and jangle gives the input which is a hidden input a name equal to this here so we're going to look for that and get it back here and store it in the csrf variable so now that we have the references to these objects what we can do is add an event listener to the form so we can use form. addevent listener and we're going to listen for the submit event and when we get that what we're going to do is first of all we're going to prevent the default action with e do prevent default so in JavaScript when you have an event listener you can pass a call back function that optionally will take the event itself as a parameter we're taking that event we're calling it e and we're calling the prevent default method on that event so that's going to prevent the submission being sent to the server and reloading the page so that's the first step the second step is going to be to actually submit the file using an Ajax request so what we're going to do is set up a variable here called form data that's going to be equal to a new form data object we're also going to set some headers here and that's going to allow Jango to accept an Ajax request so we need to add the csrf token because it's a post request so we're going to add a header here called x- csrf token and we can set that to the value of this input that we pulled out here on line 18 so let's reference that and to get the value we can use the DOT value property on that input so we'll add these headers to the fetch request that we're going to send in a second what we're now going to do is take the form data that we have and we're going to call an append method on that and we're going to append to a key called file here what we've got from the file input so the file input when the form is submitted should contain the file that's been selected by the user so we can get the files by referencing a property called files and do files is a JavaScript array so we can index in at element zero in order to get that file that's been selected by the user now the form data we're building up we're going to send that as the body of the post request that's why we're doing this we can finally do that now by sending a fetch request and we're going to send the fetch request to the submit URL for the form so let's paste that in here and then as a second parameter we're going to pass a JavaScript object of configuration and data so the method of this is going to be a post request so we're telling fetch to send a post request and we're going to add the form data as the body so let's add that as the body key and finally we're going to add some headers here and set that to the headers that we defined on line 23 and that's adding the csrf token so that D Jango knows what that token is and it can validate that token now when you send a fetch request you get back something in JavaScript called a promise you can use dot then to accept the result of that promise so we're going to take the response in this case and we're going to convert that to a JavaScript object with response. Json and then after we've done that response. Json is also going to return a promise but when that resolves we can get back the data and I'm just going to console. log that data to the browser console so that's quite a bit of JavaScript I hope that makes sense we're getting a reference to the form and then we're extracting the submit URL from that form we're also getting a reference to the file input and the jangle csrf hidden input then we add an event listener to the form that we have we're listening for the submit event we're preventing that being sent by default to the server and we're then creating a form data object and appending the selected file to that object and sending a post request using the fetch function and adding that form data as the body so what I'm going to do is just save all of this and we're going to go back to the page that we have here and I'm going to refresh this page to bring in that new JavaScript and I'm going to select the file again now before I submit this let's bring up the browser developer tools and go to the console and let's hit submit here and see what we get back now we're getting back a response from a rest framework API endpoint and that contains a link to the CV that's been uploaded as well as that time stamp that we added earlier so this time we're uploading the file over an Ajax request using a form data object and adding that to the body of the fetch method and then jangle rest framework in this case is accepting that request it's paring the uploaded file from that and it's returning a response to the client containing the link to that file and the Tim stamp and if you look at the time stamp 1931 if you go back to the Django admin for this user and refresh we expect to see that updating at the bottom and you can see that it has so we've sent this post request and that's an Ajax request and rest framework is able to handle that file upload and it's storing the uploaded file on the fil system on the server and returning a response to the client and if we go back to the page here and we look at the network tab you can see the fetch request that was sent and here we have the response data now if we look at at the headers that were sent along with that request we can scroll down here to get the request headers you can see the content type is set to multiart form data and it turns out when you use a fetch request like this and you set the body to a form data object it's automatically going to set the content type header to multiart form data so we now have an API that allows us to upload files from a client to jangle rest framework and then those files are stored on the local file system and also linked to the user that uploaded the file now a natural extension of this is to go to views.py and rather than hardcoding request. user what you can do is of course actually authenticate the user and then send the request to the API with a token that allows jangle rest framework to identify who the user is that's out with the scope of this video but if you're interested in more let me know in the comments otherwise thanks very much for watching this video if you've enjoyed it give it a thumbs up and subscribe to the channel for more and if you're enjoying this content or finding it useful consider buying the channel a coffee we have a link in the description check it out and we'll see you in the next video
Info
Channel: BugBytes
Views: 3,012
Rating: undefined out of 5
Keywords:
Id: 05YafeAFITA
Channel Id: undefined
Length: 32min 39sec (1959 seconds)
Published: Mon Apr 29 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.