But how do DJANGO signals work?

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey there it's justin in this one i'm going to be breaking down how to use the built-in django signals that is pre-save post save stuff like that now it's actually pretty simple of a concept but before we jump into it i want to just illustrate a use case that really lends itself well to django signals and that is welcoming brand new users right so you have this new service and you're like i want to send them a welcome hey thanks for being here appreciate it something like that now when a user actually signs up well where do they sign up well probably on a view and there's probably a form rendered there so those are a couple options of where you can actually send that welcome email but that actually falls apart when we go into the admin like what if i added a user in the admin or in a different view then i gotta rewrite all that logic and repeat myself that's not good instead what you can do is use a signal so every time a model is created you have the option to handle that event in some way and we do that using signal so the user is created right before it's saved into the database a signal sent and then right after it's saved to the database a signal sent right before it's deleted from the database a signal sent and so on right there's a lot of really cool things about that so that's what we want to break down in this one it's going to be fairly basic as far as how these things work but i do recommend that you have some familiar with django and actually building models already prior to jumping to this one otherwise you're going to be incredibly confused so let's go ahead and jump in [Music] all right so before we actually implement signals and receiver functions let's actually take a look at a very common use case inside of a django project and that is of course creating and saving data in the database i'm using the user model in this case but you can use any model that you have inside of django that's also in your database so what's happening here so when we call the model.objects.create what happens is a signal is automatically sent called pre save and with this it actually sends the instance itself we'll see this in code in a moment now there's also another one after it save and this is called post save it will send the instance as well also a flag called created now in this case the user.objects that create is it going to be a created instance is it a new field in the database or a new row in the database the answer is true right so every single time these two things will be sent now the pre-save well there are some caveats to the pre-save on create which i'll get into in a moment the next thing is we can actually take this instance and save it again and you might be wondering hey do these things get sent again well absolutely literally every single time so this now the post save is now a false right so what we can do is we can actually take what this data is and put it into my handler or some sort of function that handles these things right they're not always the same but this is actually my receiver right it's a receiver function that we'll end up doing so this is kind of the idea but it's not just for save there's other things that we can do as well so instance dot delete this also has another kind of signal and that is pre delete and post delete so the idea here is that it will send various arguments that are related to that kind of signal now all of this stuff is really clear in the django documentation so if you look for django signals often you'll come to this page and you'll see the various very common signals that we did we just talked about the pre-save and post save pre-delete and post delete so if we actually look at any one of these signals individually we can see the things that are automatically being sent here we're going to see this in code in just a moment but you see pre pre-delete post elite has these things and if we go to pre-save and post save we see the instance and created right i wanted to make it very clear that these are the things that are automatically going to be sent when we run these different methods and of course if we use a different model it's going to use that different model as well so the instance itself will always be of this class now it does start to get a little bit more complicated when we try to go into many to many fields this is something i'm not going to be covering in this one just going to be focusing on the pre-save and post save because that hopefully will clear up a lot of issues that you might be having with signals themselves but if you understand where we're at so far then i think you're ready for the next step which is actually handling these signals now let's go ahead and turn these examples into real code inside of a model stop hi now this is actually the best place to handle signals inside of a model stop high inside of an app that is also in your installed apps and it has the migrations you've done all of those things already now we're actually ready to handle one of these signals now it actually doesn't matter where you use these signals and i'll explain why in a moment but what we want to start out with is the post save signal so the import is from django.db.model this has always been the import but it's certainly possible that this can change over time but the signals this concept is not going to change it's always going to be there so this is what we're going to start with you can ignore this receiver for just a moment but what we want to do here is actually handle the event that a user has just been created so i'm going to call this my user created handler okay and i'm not going to put any arguments yet and i'm just going to go ahead and say pass and now what i'm going to do is actually connect my user model to this handler there's actually two ways to do it first off we can do post save dot connect so that is the actual signal itself dot connect and then the receiver function we want to use this is this function right here and then the actual sender of the signal as in we are connecting to a specific model that we want to send this signal and this could be literally any model on literally any models up high right so i can run this over and over again but now that i have this handler i can do what i want with it but the question of course is what are the args and what are the keyword args or more specifically what are the arguments that are automatically going to be passed to this handler before we get there though there is this decorator of django.dispatch this actually is another way to write this exact same thing so you can actually use a decorator and pass in the actual sender itself as well as the signal itself so the signal comes first and then the sender comes second so these two things are doing the exact same thing i often use this one so i don't have to remember to import the receiver function um the receiver decorator that is but i think this is actually a little bit cleaner overall you'll see this a lot more on really modern third-party packages that use signals but anyway so now that we've got this we have our connected signal so let's actually see that i'm going to go ahead and print out args and keyword arcs here and i'm going to make sure my server server's running in this case i have it in my terminal here it is running i already have the admin i already have all that stuff set up so what i'm gonna do is go into my users and i'm gonna create another user so this is a live user abc one two i probably need to actually give a decent password or something maybe not but i will add one just so it validates okay cool so i now just created that user let's jump into our terminal and what do we what do you know here here are all of the arguments that are coming through here so notice they are not just positional arguments they are actually keyword arguments in here so we've got the signal itself right so a model signal we've got sender okay so that's a user we have the instance the actual user instance whether or not it's been created right the flag for created and all that pretty cool and also pretty simple now if you go into the documentation it shows you all of these things these are the arguments that are sent with this signal so most of the time what you'll often see is it's set up this way so sender instance and created that's it right so this is where we could now have the flag of if created print you know i'll go ahead and say send email to and then user.username right so now after it's created then we're gonna send that email okay if it's not created so we can say else in here obviously i'll just go ahead and say user user.username was just saved okay now you might be like well wait a minute user is not defined anywhere in here let me break this down and we see that user is not defined well of course user is the actual instance because we know it's a class here right so it's of the user class so we know various things about this user so i can actually just come in here and do something like that i no longer need to print out the arguments that are coming through but of course as you know all of the additional arguments will be captured by these things so these are the main ones that we want to have in here so we'll go ahead and save this and now what we're going to do is just literally save this live user this time i'm going to go ahead and put my name as the first name for justin okay so now i've made some changes i hit save and if i scroll down a bit live user was just saved right and if i do it again let's go back into that user and hit save let's go back into any of our users and hit save we now see that these signals are being sent this is really cool and really the fundamental part of all of this which is really nice okay so there is another signal that we definitely want to talk about and that is pre-save okay so pre-save is a lot like post save but different in one major thing or actually two major things first off pre-save does not have this created argument so created will only be known after it's actually saved in the database so this remember pre-save is you know before saved in the database and then post save is after saved i think that's really clear with the name of the signal but in case it's not we're going to leave this in here right and that's why the pre-save doesn't have this created flag because it doesn't know if it's saved in the database or not that actually could fail you could try to save something in a database and it could fail absolutely also depending on what you do here so one of the other caveats about this is if we print out instance dot id well this might be none right because you won't get an id you won't get that primary key until it's actually created in the database so let's go ahead and see this i'll go ahead and say instance.username and instance.id so we can actually see both of those things in action if i go to one that i already have i can save that and sure enough i will get uh both of those things oops we need to make sure that everything is saved and ready to go okay so let's try this again i'm gonna come in here and just hit save and continue editing and we'll jump back into the terminal and it's still not printing ah yes so we want to change the name of this so user what i often do is actually the user pre save receiver or i'll do just user pre-save but we'll change both of these to the actual signal it's receiving okay so we save this and now we're going to go ahead and run this again refresh in here and hit save and continue editing and now what we'll see is the pre-saved signal we see that first it gives me the user and their id and then of course that it was just saved so now what we want to do is create another user really simply and i'll say another one and a whatever password that hopefully passes this ah not matched okay saved and another one none right so it doesn't actually have an id but post save would and of course you could always test that out on your own so that actually covers the example that we talked about in the beginning which was how do we actually handle these two things now of course if we wanted to not use the decorator on the pre-save we could do that here now before i actually go much further one of the things that i also want you to note as a caveat is inside of either one of these instances if i call instance.save what do you think is going to happen with this well it's actually going to trigger the pre-save as well as the post save this happens every time you call instance.save so if i do this on the pre-save i'm going to refresh in here and i'm going to go ahead and hit continue editing i get a maximum recursion depth exceeded while calling a python object what that means is this is recursive it's literally calling itself save over and over and over again okay so this is something we need to be aware of in any of our signals the pre-save or the post save are the the most important ones but the coolest thing about post save if you do need to save something in here what you can do is you can call it in the created instance so if it's just created then you can call save and it won't necessarily change a bunch of things right it won't actually recall this over and over and over again so we can actually see this as well let's make sure that it's not in here so let's say don't do this okay and let's go ahead and now create another user abc123 there we go created another user and if we look in our terminal we see that it says no id and then we sent that email now it says an id and that it was just saved so it actually triggered a save twice so when it was created and then in our signal we actually re-saved it all over again okay so that is generally how all of these signals work but now let's actually see a little bit more of a practical example with the model that i already have which is simply blog post okay so going back into the admin what i want to have happen here is when i create a blog post i want the slug to automatically be created okay so this is very very common now what i'm going to do here is add a receiver function here and this is going to take in the post save and then the sender is of course our blog post here and then in this case i'm going to go ahead and do blog underscore post post save and remember it takes in sender instance and created and then args and keyword args okay so what i can do in here is say if not instance dot slug i can do instance dot slug equals to slugify of instance that title now slugify is a built-in feature to django if you're not familiar with it essentially what it does is say this is my title and it turns it into this is my title essentially which we'll see in a moment so since it's the blog post post saved signal we still need to actually save this so instance.save right so notice it actually won't resave this again because i'm saying hey if it's not there let's go ahead and set it and now i can save it okay so let's go ahead and try this out and i'll go ahead and just use something that's already going to be pre-filled in here and i'll go ahead and hit save and continue editing i got a little syntax error somewhere and that is my actual signal itself so this should be just one star to collect those args i'm going to try and resave this and sure enough it actually creates that slug but this is actually not that efficient because what's happening now is it's going to call save twice so literally when i create it and then right after i create it i'm making that slug so a better option would be to use the pre save and the coolest thing about pre-save that i've yet to mention is the fact that i actually don't need to call dot save at all so i can comment out the post save one and now what i can do is just run it like this and that will actually set it for me i don't need to call save because save is gonna be called in just a moment right so pre-save happens right before it's actually saved so i can modify things right in line with that so let's go ahead and add another blog post here and microservices are awesome and now i'll go ahead and hit save and what i get is oh i got a positional argument that uh oh pre-save doesn't have well of course it doesn't is this right here so you know this is one of those things that happened when you copy and paste stuff so now that i've got rid of that i refresh hit continue and now if i come in here i now see that the slug is created so that's when you use post save and pre-save now another example of this would be hey notifying users when would we actually want to notify users of this blog post well this is something that we want to do with some sort of logic right and that logic should mean that yes the blog post already exists and we want to actually have the ability to update what's going on here so if we think of this in terms of pre-save what do we need to know for sure that the model has itself well i want to make sure that if instance dot id and instance dot notify users now i'm going to go ahead and print out notify users and then you also might be able to go ahead and say instance dot notify users timestamp we can use timezone.now which i actually don't have imported but that would be a way to set that i'm going to leave that out for a moment now the reason for this is now i actually don't need to do anything because well once i notify the users then we're good but really it should be and not or more specifically we keep it as an instance notify user so it has an id and we are saying yes let's notify these users so in order for this to not be saved over and over again we actually want to say it as false okay so we want to reset that to be in false so this is a way to actually trigger something in here inside of a pre-save so now if i refresh in here and hit notify users i can go ahead and hit save and it's going to be false again right so another way to think of this is to now add in that time the time zone or the time stamp we can do from django.utils import the timezone and what this is going to do is allow us to save that time zone okay so now i can actually save that data based off of this so i'm going to notify users and hit save and continue and there we go we now have a timestamp here now realistically what would actually happen is you would call like a celery worker task to offload this so it's not actually happening in our main request but it's actually sent somewhere else so if you want to know more about that check out the time and tasks 2 project on cfv.sh so the thing here is we can absolutely do this exact same thing down in the post save method now the reason i actually avoid using postsave a lot has to do with the fact that we might call save on accident and run that recursion where it's constantly trying to do this stuff it's constantly saving and it's constantly having issues with that so that's one of the reasons that i don't actually call post save that often it's only more for stuff like this slug itself right potentially if i wanted to do a bunch of things after the instance was just created then i would also have that created flag in there too so pre-save is like my go-to for a lot of signals for stuff like this but again with this instance.id perhaps that's not the best idea right perhaps i want to do it when it's created like if i'm writing the blog post and i'm like hey i want everyone to know about it right now in that case then i would say you would do something more like this and then instance.save notice that it won't be recursive it won't actually keep calling itself because i turned notify users being false where this block will only execute if notify users is true but they're actually still very much the same kind of thing so again going back to when i would use both of these this is actually probably how i would end up doing it which is just like this i have the pre-save that's building the slug for me this will happen prior to it being saved for sure it doesn't rely on the instance it doesn't rely on the fact that this is absolutely saved in the database what it relies on is literally just another field that is absolutely required so you'll get some sense of doing all these things once you do it a lot more yourself in projects but the idea is you just definitely need to test these things out and just be aware of the fact that if you save it well you're going to run into some issues you can run into issues i should say but you should also try to run into those issues you should try to see how that actually works so i think you now have a really solid foundation to use pretty much any signal that's built in to django so let's go ahead and take a look at delete right so pre delete and post delete now these signals themselves are not really any different than what we have here and the idea is when would i want to use both of these things right so let's go ahead and copy our other signals from blog post this time of course i'm going to change pre-save to pre-delete and post save to post delete and post elite of course does not have the actual created ins argument in here but now we can just see you know print instance dot id has been removed something along those lines so same thing up here and will be removed cool so this uh pre-delete i might want to move or make a backup of this data right so if you for some reason don't accidentally want to delete your posts you can make an entire other model or even the same blog post model and have another field in here saying like instead of active you have it as like a backup or something along those lines now i will say that delete is often if you want to override the delete method you can absolutely do that right on the instance method itself but unfortunately this doesn't always cover every kind of delete so that is something to keep note of but if you wanted to do something with this data when it's being deleted or after this is how you go about doing it and of course just like the other signals we can actually call it directly with elite.connect and then the same kind of information that we have up here right so two different ways to do literally the exact same thing so i actually don't end up using post elite a whole lot unless i'm trying to back things up and i want to keep it where this backed up data is aware right the other thing is if you have a manager for example and that manager needs to be alerted of something that one of the staff members deleted or at least known about it you could do that as well uh another thing is with the post elites perhaps you have a notification you want to send to your user saying like hey that giant process that took a really long time to delete was now deleted so there's another kind of potential option that you could do in other words basically taking a celery worker task to do a massive big delete right but i think you now know how to implement these things and i would encourage you to test them out so i did mention that i'm probably not going to cover many too many but i decided since you're sticking around you've gone this far let's actually cover that many to many stuff now we're going to go ahead and implement the mini to mini field signal but one of the biggest caveats of this signal is the fact that it can happen on both sides of the equation it can happen on the actual blog post instance itself or it can happen on the user instance itself we are going to be doing it on the blog post side so when something happens on the blog post instance itself that's when we'll be handling the signal not the reverse side as in where the user is so this is a little tricky if you're not familiar with how many too many fields work but i'm gonna assume that you already are familiar with those things even if you're not you can still see just generally speaking how this is implemented so what's gonna happen here is when we go into this model anywhere in our code and we add or change something about a mini too many fields we want to actually see what happens with that event so we can do all sorts of really cool things so to do this i'm going to go ahead and import the m2m changed signal and then i want to of course make a receiver function for handling this so i'm going to go ahead and add at receiver and sender being blog post and then define blog post liked changed so the reason i'm calling it liked changed is because the user lite or something about this mini to minifield is what's going to be changing so i actually am being incredibly explicit about the field that i'm using in this case i just you did the entire blog post that's not accurate we actually want to use dot light the actual field itself and then dot through so this is the way you actually connect that so whatever the mini to minifield name is whatever the actual model name is and then you use dot through that is standard so in here now we'll see sender and well actually let's print out the args and keyword arcs just so you can see what those things are and then you can pretty much take it from there with the documentation so let's go ahead and print out args and keyword args okay so now it should be nice and mapped up so let's go ahead and give this a shot and right now i have two users on there okay so let's go ahead and comment out those two users and i'm going to go ahead and save it and i get print takes at most four keyword arguments oops i did a mistake i shouldn't unpack these on the print statement oopsies okay so let's go ahead and try that again refreshing here i think that data has already changed so i'm going to go ahead and run this again so i'm going to hit save and look back in the terminal and here we go here are all the arguments now granted they are all keyword arguments and now we see what's going on we see the sender right so the sender is coming from the liked field no longer the actual model itself the action pre-add host ad look at that you actually have signals for pre-adding to this field and post adding to this field much like post save and pre-save so the cool thing about that is then in my actions or rather in this signal receiver itself we can do sender instance and action and this we can now go ahead and say if action equals to pre-add then we can print out was added well what was added let's go back into our print statement here well we have the instance the actual instance of the main class itself but we also have the related class so that is the model itself so this is the user model that's the related class itself and the pk set as in the primary keys that are being changed remember it's many to many so it's going to be a bunch of different things so to actually get that data what i can do here is say query set equals to well what's the model that we're going to be using that's literally this model so it's quite literally the other model and i can use keyword args.get of that model dot objects dot filter and pk [Music] and we'll go ahead and say n and then this is going to be the actual set itself so that's the pk set so again we can do keyword rx.get and pk set and then i can print out q s dot uh count right so let's go ahead and get rid of the these print statements right here we'll save that and i'll refresh in here and we'll go ahead and add just one of them so just live user so i now removed some and added some so let's go ahead and save it i have no errors and was added is one okay so there was the action of remove right so if we actually printed out that action i would have been able to see those ones that i removed as well so if i refresh in here again and remove a user add a user and hit save i'll now see that all of these things pre-remove post remove pre-add was added all that stuff post ad cool so um a number of things that are going on here that are really nice number one this keyword args get well of course i can actually just declare model and pk set then i actually won't have them in the keyword arguments and i would just use model.objects.all and then you know pk set like that but i personally use it like this more often than not because i don't always need the other field i don't always need the other class these are the things that i probably always want to be aware of but it's nice to know that these arguments are in there and of course if that context is a little confusing at all just go into the documentation again and look for the many-to-many changed and you'll see all of the things that we just went through right so reverse that's the other side of the model the actual model itself primary key all of that stuff it's all in there pretty cool i think um so that's how you do many to minifields now i will challenge you to take this idea and go one step further by using all of the actual signals themselves so class prepared pre-init post in it those things aren't a whole lot different than what we did but it is nice to know that they exist now there are other signals that are cool as well as in pre-migrate and post migrate so when you run your database migrations you can totally do a lot of those things and then also the request response signals those are also kind of cool as well now i actually use the model signals probably the most because of things that i just showed you with the practical implementation of something like notifying the users or actually using the slug and sluggifying the instance now i will say the biggest caveat to all of this stuff is you actually don't have to use signals to accomplish all of these things it's just a really clean and easy way to execute this the other thing that's important to note is you can actually use signals to do all sorts of cool things so in this slugify signal i could totally use other models if i wanted to like abc and you know another one that use that same sort of slug instance itself right so this actually allows me to connect to it multiple times on all of these different models this actually does make your system a little bit more complex and probably harder to follow so i often don't do this method so i don't actually recommend that you do either most of the time i'm actually doing it right in line with the model itself so that model i know what's going on but for those of you who are a little bit more advanced you can absolutely do all of these things as you see fit the other thing that's really cool is creating your own custom signals yourself so that actually isn't that hard either and it does handle the things the same way but it is nice to have something that you can now broadcast these messages across everything hey thanks so much for watching this one hopefully you got a lot out of it now if you liked this deep dive into this django concept let me know in the comments other concepts you want me to dive into whether it's django react python javascript electron tensorflow whatever it is let me know in the comments and we'll see about another deep dive in the future thanks so much for watching and i'll see you next time [Music] you
Info
Channel: CodingEntrepreneurs
Views: 41,820
Rating: undefined out of 5
Keywords: djangourlshortcfe2018, install django with pip, virtualenv, Django Web Framework (Software), Mac OS (Operating System), Python (Software), web application development, pip, django, beginners tutorial, trydjango2017, install python, python3.8, django3.0, python django, install python windows, windows python, mac python, install python mac, install python linux, pipenv, virtual environments, beginner python, python tutorial, django signals, signals, post_save, pre_save
Id: rEX50LJrFuU
Channel Id: undefined
Length: 35min 47sec (2147 seconds)
Published: Wed Jan 27 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.