Learn Django - Extending the User Model, Using Signals and Building a User Avatar Feature - Part 9

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello and welcome back to another tutorial so here we are in building a blog or building a simple blog series part nine so in the previous tutorials we've been building on an authentication system it's not perfect it's not worthy of any production website but it's a start to develop our knowledge of the authentication system so as promised i said that i would follow that tutorial up with some other smaller tutorials whereby we extend and look at how to improve upon it so in this tutorial we're going to extend the django user model using a one-to-one link and then develop a user profile image system or facility or service or feature that's probably a better word so yeah we're going to create a new model to store the user data that's the first step and then we're going to i'm going to introduce the idea of receivers to perform actions on events and this is a really powerful tool because now we're really getting to a point where we can see that we can actually hook into what django is doing in the background and we can actually then extend upon that so that when django performs in action in this case performs or creates a new user we can override or we can add new features so that other actions are performed so we're going to use a receiver here so that when we create a new user it also creates a new profile for that user of course once we've done building the model we're going to then delve into the form and the view and save that new data so we are working from the previous tutorial tutorial number eight where we built the authentication system there is a link so you can download that there is a link to a video which shows you how to actually create or start the application so look at that if you need it then the final step in this tutorial is that we're going to build a user profile image feature so the user can upload a avatar image and we'll then apply that as their user profile image so i've gone ahead and downloaded the previous tutorial code part eight and i've started the code you can see that we can run it directly and you should now be able to for example log in so you can log in as i think it's test and test so let me just log out it doesn't work log out so test and test should sign you in so at the moment when we go over to the edit the details we can see that all we have is first name last name and email and obviously that information is coming from the user's model the default information we have or the default information we can save about the user so we want to extend this now and add more items because no doubt you want to store more information about the user so we created an accounts application so we're going to use this application and go into the which doesn't exist yet in actual fact we're going to create a models file so we'll create a new file here called models.pi so we're going to build a new model and extend from the user's model so to start off we're just going to import the django models so that we can work with models and build models and then we're going to import the authentication model the users table into this project so i'm going to call my new model just profile and i'm going to make a one to one connection between this table and the users table so we do that and we use on delete cascade which means that when we delete a user in the user table it should also delete the user in this profile table so let's add a new field for the user profile image so i've just called this avatar so here we're using the image field and notice that i've included upload to attribute so we're going to use the user directory path and the default is going to be user avatar jpeg so in a previous tutorial if you remember we created the media folder so if you aren't working from this project you're going to need to install pillow so here we're using pillow 7.2.0 that's going to help you manage images and then you're going to need to go to the settings so in this project the core is the default application we've got settings and at the end here we have some settings here to the media root and media url so here we're using a folder called media to store our images and we define like i said the media root and the media url so here i'm saying upload to and i'm now going to create this method user directory path to define exactly where i'm going to store the data so you can see that the default path is user avatar so that if there's no image that's already been created for this user if the user hasn't uploaded an image then it's going to use the default so there's always going to be an image available for the user to use so i can go ahead and create a new method here and connect it to this user directory path where i'm going to create a custom a custom path to store my image data or to store the user's avatar images so i'll go ahead and build this so it was called user directory path so i bring in the instance and the file name into this and then i'm going to return the actual path so it's going to be stored inside the media folder in a folder called users and then avatars and i've got these other attributes 0 and 1. so i then define that format so the first of all 0 is going to represent the instance user so the instance this instance the user and then obviously this is a one-to-one connection so i can then go into the user model and access the id and then the file name so i'm going to store an image that the user uploads to the the media folder users folder avatars folder then a custom folder based upon the user id and then the file name so that's how the images are being stored so let's just finish off by adding another field here you can obviously add as many as you like so i'm going to add a bio field here where someone can type in details about themselves so let's go ahead and just access the admin let's just register our model so it's accessible accessible within the admin area so now we should be able to go into the admin area and access our model so back in our application i'll just go to admin you can now see we have the accounts profile model and inside of here we're now receiving an error so no such table accounts profile so let's just go back into the code and work out what the problem is it's very simple um the actual model doesn't exist yet so there was a pause there um i was hoping you're gonna try and work it out honestly so the problem is obviously that we've created a model but we've actually yet to apply it to our database so we can now go ahead if i just close the server i can now go ahead and make the migrations so first we make migrations as per normal i can spell it right and then you can see it's going to create a new model called profile so now we can go ahead and migrate so at this point you may receive an error because i've kept on purpose in this series the data to a minimum and i've tried not to build this up of course you may have used to like some of the previous code to build something and you could start to receive some errors here because we're dealing with the fundamentals of your tables here in your databases obviously there's dependencies between different tables so here there could be problems for you um it may be that you're gonna have to kind of export your data re-completely rebuild your database and then import the data back in for me you can see that it works absolutely fine so now we can go ahead back in the administration we can now go to accounts and profiles of course we have no profiles available now i just want to show you this so if i go into the home for example go to users let's just create a new user so what we would probably want to happen is that once we save a new user we would want to have a profile set up for them automatically so if we go back to home and profiles you can see that we don't have a profile available so what we're going to do next is utilize a receiver signals in django which is going to essentially when we create a new user it's going to capture the that signal the fact that someone is trying to build or create a new user then it's going to automatically create a profile entry in our profiles table that corresponds to that user so it's worth definitely reading through the signals in the documentation it takes you through all the different steps to build signals or receivers so that we can perform actions when django does something in the background okay so when event occurs in the background so receiver functions so first of all we need to define a receiver function so first of all let's just make sure that we've got all the right dependencies for this so we need to import from from the django.dispatch the receiver so that's the first step so now let's go ahead and build our receiver so let's go ahead and build our receiver so there's two properties here first of all we have post save so the manual tells us it's like pre-save but sent at the end of the save method and then secondly we define the sender which is essentially the model so here we're using sender equals user so essentially this receiver is waiting for the users or new users to be saved when a new user is saved a signal is sent across to this method that we're going to build and then it will perform the action that we have in the method so we go ahead and create our new method here and then we pass in some attributes here so we've got the sender which is the user and then we've got the instance and then created so back in the documentation here you can see that we got the sender the model class the instance the actual instance being saved and created a boolean true if a new record was created so at this point this is important created because this is a boolean so essentially what's going to happen here we can we're now going to check to see if created equals true because if we make a new user this created here will be flagged as true it's a billion true so we can check that and then if it is true we can then perform an action so we create if created so if it equals true we're then going to go ahead and profile objects which is obviously our a model here and then we're going to create a new user instance so we just say user equals instance so we just create a default instance or blank instance of we'll create a blank instance entry into our profile table for the new user okay so i apologize if that was too much information or not enough information hopefully by linking and showing the documentation you can read through that and it is fairly self-explanatory what we're doing here when you read through that but just have the overview an underpinning idea what's happening here like i said um when we save some data in the user table um that we're gonna have a flag that's true we're gonna check to see if the flag is true if something has been created if something has been created we're just gonna create a blank instance a new entry in our profile table for the new user so we're not actually building a new model so we don't need to migrate or make migrations etc so let's just go ahead and see what happens when we actually have this in place so if i just go back to the project here i'm just going to log out and attempt to log back in again i need to start the system so if i try to log in as test test we're okay at the details we're okay so let's go ahead now and go into the admin area like we did before we're going to build a new user so i'll just delete this user and we build a new user and they should now have an instance whereas before they didn't in the profile table so i add a new user okay and then press save so a new user was created so now when i go into the profiles you can see that we have a new user in there so at the moment obviously it just says profile object one of course we can change that very quickly by going into our model so using our classic dunder here so we're just going to return the user and i'm going to drill down into the user table and which we can't see and then access the username so i'm going to just apply that and go back and press refresh yep so we can now see which profile it refers to so i'll go into here and we see we've got a blank entry here you can see that the avatar is using currently the user avatar jpeg which was the default item that we created so the user avatar jpeg so that's all working absolutely fine so for those who haven't been following the series we've been building or the previous tutorial we built this system here so i'm logged in as test test you can see that when we go to the edit details we've got this here so let's go into the code and just access that so we're inside the accounts forms and the bottom here is a little bit of a mismatch of code here in actual fact because i was explaining different things so you can see that i've got the first name last name and email and defined and we're setting our attributes and we're doing things in different ways here so that's currently the code for that particular form so what we want to do here is not necessarily utilize this form but we're going to use a form within a form so let's go ahead and define a new form for this data so we go ahead and create a new class i'm going to call this user profile form and we're just extending from model form so we're just going to have django build our form for us so we're going to use the model profile and then the fields bio and avatar so we go ahead and i'm just going to create some a widget here so we're just going to add some style here because we're using bootstrap so the bio bio we're going to have a text area that's going to be a text area and then we're just going to have five rows and use the form control class from bootstrap so that's in place uh of course i'm using this new model so i'm gonna need to import that model in so from accounts cancel that from accounts dot model we're going to import the profile model so let's now move across to the view and see what's happening in the view so we had um it was password protected so we had edit here so at the moment we receive the request method post and then we deal with the the form so the form is the user edit form so that was the form that you currently see and you can see here we checked if it's valid then save the data else we just showed the form so that's what's happening at the moment so we're going to put the form inside of the form and we're going to save the user form and the profile form at the same time so let's go ahead first and import the model i don't think i've done that yet so we've imported the model profile to make it available and now we're going to go ahead and just extend this so let's start off by importing the actual form and then just making sure that the model is available and then we go down and create a new entry here say the profile form equals user profile form and obviously when someone submits via post we want to capture the information so we're going to get the post information for this and also in this case they're posting files so we want to also capture that so in this case we now want to validate whether both forms are valid so instead of if user form is valid we can just extend so we can say if profile form is valid and user form is valid and then we can go ahead and save both the user form and then we can also save the profile form so we save the data in the corresponding databases so in addition to that obviously we want to output not only the user form now but we also want to output the profile form so we go ahead and do that just tap that in so we can now output the the form if we don't receive a post request the user will just see the form so we then return so let's just move this across so we then return and then obviously we want to also now return the profile form as well as the user form so we can just extend that with a comma and then add in the profile form which obviously correlates to this entry here so now we have that in place we can then go ahead to our template so you'll find the template in the templates and then it'll be in accounts and then we've got the update.html page so again a load more code that we pre-written in the other tutorial so down here you can see we've got our csrf token and we're using labels and we're extracting information out individually from the form and placing it on the page just to add some more styling so what we're going to do before we save the changes we're going to add a new entry just below the email form where we're going to add the bio and the the option to allow the user to update or to to upload a user image so let's go ahead and first of all add a label so we're just extracting the label from pro profile form so profile profile from bio label so we get the label for the bio and then we output the actual input or the text area in this case remember we define the text area in the form and now we add a new div element here with form group so because we're just following the bootstrap pattern here we add a label um for our avatar and then the profile form avatar which is going to show the button that allows us then to upload a file so it's as simple as that so now we can go ahead hopefully and have a look and refresh and we've got an error user has no profile so we were expecting this problem because we're currently logged in as someone who was the user was created before we created the profile system so they don't have a profile so let's go to admin and remember we're currently signed in as test so if we go to profiles we don't have a profile so if you are utilizing this code you're gonna need to potentially recreate some users in order for to get this code working so we could potentially add a new profile so i just select test and add new bio information and then press save so we can save it like that so let's go back into the the area uh into the edit details and you can now see we have the bio and which i've just written new bio and we have the option here so we're currently using the default which is the avatar jpeg which doesn't exist just yet and we have the option now of uploading a file so go ahead and select a file so i've selected i've chosen a file it's called untitled1 jpeg and i'm going to press save and you can see that i'm now being asked to fill this in this is just because we need to get rid of the required in these sections here um so i press save and i didn't receive a message saying it was successful so potentially there is an error here so so because this is a a user we created beforehand there's just a few things that maybe we want to change here so let's just go back into the form area um at the bottom here i was explaining a previous tutorial about this clean email i'm just going to get rid of that because logically it doesn't really make sense that we're trying to evaluate every single time whether the email is unique or not so i'm just going to remove that from there and then go back in just to do a refresh here and you notice that it refreshes what i previously done now it says details successfully updated so let me just show you again test test i'm going to choose a file so i choose the same file as before press save now it says details successfully updated so let's go back into the admin area and we should now see this new information so you can see that i'm using this file that i uploaded now this is one of the things that i haven't implemented here the fact that we can upload multiple images so notice that when you upload an image it uploads the file type or the file name that the file is given but if we upload the same file again django will put on a random string there at the end of the file now what we want to do really is create a function so that we delete the previous file and use the new one so that's something you can develop so we can see that it's working so let's just check the folder to see where the image is going so if we go back into the code we can now see if we go into the the media file folder we should be using the users avatars and then 17 is the id of the user so that uniquely identifies the user the user is id number 17. so that's the case and now we've got two images and notice that we're using this image at the moment so next up we want to replace the existing bio image here which is just some css in a circle we're going to replace that with the image that we're using so if you remember in the previous tutorials we created a drop-down list here by utilizing in settings so if you go over to the core and settings we created a a new view we made it available within the whole project by placing it within the templates here so this view is available accessible on every page so we're gonna do the same thing again but utilizing the avatar there so we're gonna make the avatar available on that navigation bar which is obviously placed on every single page so let's go ahead and access our views so at the top here we're going to create a new view for this so i'm going to protect this view so the user has to be logged in for this view to be accessible so i'm just going to call this avatar and then i'm basically first we're going to get the user id and then i'm going to use that user id to select the avatar from the profiles table user equals user and then i'm going to create a context so this is the data i want to send back so the data that's inside the avatar there so i return that as context so now i can go ahead and go into the settings and i just want to add a new entry right here so let's just type that in so that's going to be accounts dot views dot avatar so that's the view that we just created called avatar so now we just need to replace the image so the good thing is i created the css beforehand so it will be placed in a circle so let's go ahead and find first of all the template and it's in the base and we're using the nav main so at the moment you can see i've got this css here it creates that dot but we're going to utilize this to actually um build the circular image so this is where the dot is at the moment so we can remove that and now we can just add our image so we're already in this section where the user has to be authenticated by this if statement here so let's just go ahead anyway and maybe we want to run this code uh only for users that are authenticated so we first of all create the image tag and obviously we're gonna inside the src place some data so here i'm just gonna use some bad practice i'm just gonna loop through the data and select the data so for data in the avatar i'm just going to extract the avatar as the url so i go ahead and do that so obviously i don't need this here so let's just get rid of that so now we're looping that data leaving through that data and we're just extracting the avatar url inside of the image so let's go back to our page and refresh and you can now see we've got this square image that we uploaded this was the image that we've uploaded so now we can go back and we can now just change the class up here from dot to avatar and that's going to perform all the actions there and turn it back into a circle so now we now have an avatar so the last thing i want to show you is just some of the um settings that can be applied to this because we're uploading or allowing users to upload files this is always a tricky thing security wise and we want to try and avoid allowing the users to do anything like this whenever we can because it means a little bit more work for us so this might be better linked to a third-party service an image rather than allowing people to upload files of course we can manage it but if you're building a small scale website like this potentially it's just a bit potentially more code that you have to write but we're just demonstrating some of the principles and features of django let's not forget this is not a production level website at all so let's go ahead and think about some of the validation checks that we can make so we're going to make some validation checks inside the model in this case so what we're going to do is we're going to check to see if the user has uploaded the right file of right size of image so we're going to create some validation errors or validation yeah some validation errors so um we're going to first import the validation error so we can build some validation rules and then we're going to build a a new function here so um inside or just below our model let's create a new method here we're going to call that clean so first up i create a simple if statement here so if not obviously if there is no avatar then we're just going to raise an error so that's the first thing that we can do but importantly when we want someone to upload an image we want to define or try and limit what the user can upload and if you think about user image sizes we don't want the user uploading a 4k image just for a profile picture so we want to limit them and we want to define how the image should be sized before the user uploads so we're going to first of all go in and add a new dependency here so we're going to include the get image dimensions because what we're going to do is when the user uploads the image we're going to check the size if the size doesn't match what the what we expect it to be then we're going to raise an error and tell the user that they need to change that image to a certain size before they upload it so let's just go ahead and do that so next of all we want the else so if and then else so let's start off by getting the the width and the height of the image that the user has uploaded we then go ahead and create a new statement so if for example the width is not 200 pixels wide then we're going to raise an error and then we'll do the same thing again on the height obviously you can change the parameters here so if the height is not 200 then we're going to raise an error so that should be it for now so let's go ahead and test this i'll just quickly change these numbers because my image is 200 by 200 so i change it to 100 by 100 i'm gonna try and upload a 200 by 200 image so if we go ahead and test that out at the moment it's saying that the detail successfully updated which is obviously incorrect so there's a few changes if you go into the accounts template accounts folder template and then update you'll see in here that at the moment we're trying to validate the form if the form is validated we're doing something here detail successfully updated but we want to update this now because not only do we want the user form to be valid we also want the profile form to be valid so we'll go ahead and do that so now we're testing both the user and the profile form is valid before we return details successfully updated and while we're here let's just add in a an error for if the profile form returns an error we're just going to say file size incorrect has to be hundred by a hundred so here we're just blanketing the error we're not going to use the the error that we've generated in the model so we try this again we're create a file or choose a file so i know this file is way too big so i press save changes and now it says file size incorrect has to be 100 by 100. so you can see that the validation error is working and we return the error here so that we have to actually upload a 100 by 100 image so let's just go back into the let's close this let's just go back into the models and i'm just going to change the 100 to 200 because i know i have an image here that's 200 pixels by 200 pixels so i go ahead and choose a file um called untitled one which is 200 by 200 press save and you can see that we still receive an error now the reason why that was because i didn't press save so i press save and i've tried again and now we can see it says detail successfully updated in addition to that you can now see that the profile image has also been updated to the image okay so there we have it extending the django user model using a one to one link and we developed a user profile image or avatar and we obviously did some form validation or some checking of the image so there's obviously lots more that you can do here to validate we could check for example the file type that the user uploads um obviously for security reasons obviously users can upload a jpeg but include different code so there's obviously different security issues here like i think i alluded to but overall this is a a baseline an idea a way of performing this function so we created a new model to store the user data and we used receivers to perform actions on events that's a really powerful tool that definitely look into and have a read we developed the form and view to save a new data so we looked at different parameters there and of course we created a new profile image feature and we added some validation to ensure that the image size was what we wanted before the user uploads it to the system so again thank you for listening hopefully you got to the end if you have thank you very much and i'll see you in the next tutorial
Info
Channel: Very Academy
Views: 1,662
Rating: undefined out of 5
Keywords: django signals, django extend user model, django user profile, django user avatar, django user image, django profile image, django user profile image, django 3, django, django tut, django tutorial, django examples, django authentication, learn django, django beginners, beginners django, django user model
Id: aydAZzhHgBA
Channel Id: undefined
Length: 36min 9sec (2169 seconds)
Published: Tue Aug 11 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.