DJANGO CHANNELS 2 Tutorial (V2) - Real Time - WebSockets - Async

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

will check this out - been meaning to learn websockets for a while

๐Ÿ‘๏ธŽ︎ 3 ๐Ÿ‘ค๏ธŽ︎ u/djob9601 ๐Ÿ“…๏ธŽ︎ Jul 08 2018 ๐Ÿ—ซ︎ replies
Captions
if you've been working in Django you'll know that there's this really cool capability to do Ajax that is having your front-end communicate with your back-end your front-end doesn't have to reload this is of course not unique to Django but one of the things that you might be wondering is how do you actually have your back-end initiate something with your front-end or some sort of communication with your front-end how does that happen now this is all done using WebSockets and we can do it asynchronously using django channels so asynchronously doing WebSocket communication with django channels and python 3.5 and above that's what we doing in this one so let's go ahead and jump in what you see right here is the absolute requirements to get this going and I'm also going to assume that you're not a beginner in Django if you are proceed at your own risk especially if you're using a different version of any of this stuff now what I also recommend that you do is go to github right now and go through the recommended start if you go through that recommend and start it will get your code exactly where our code is at the start of this video and that way we can all work on the same thing things will change over time and this is a way to ensure that they're not going to change for you while you're working through the video and of course like always use the documentation as reference when you need so let's go ahead and jump in to our code now that we've done the recommended start I'm gonna go ahead and integrate channels into our project and I'll give some extra context that's sure it is in the documentation it's in the installation portion but I'm also going to do something that's a little bit closer to being ready for production so pip install channels two point one point two yes you can use a different version just stick with the docks if that happens I don't recommend it because yeah things happen we use different versions and that's not really the point here the point is to understand how channels works inside a jingle so pip install two point one point two and it'll install all these other extra stuff that we need really cool I love that about Python okay so now that we've got this let's go ahead and bring in channels into our installed apps okay and channels go now if I go ahead and run Python managed to py migrate of course certain things might happen here but the main thing is I want to go ahead and run the server now I don't have an a SGI application set so right off the bat Jango channels has taken over my run server command so keep that in mind if your third-party packages use run server for some reason Jango channels might not play well with them so it is because it's a it's a major departure and it's in the fact that it's doing this has to do with a lot of things related to the asynchronous code but also how the WebSockets work and I mean there's a lot of stuff that's going on under the hood that means that we absolutely have to set our a SGI application so what that means is that I have to create the routing of for my a SGI so in other words we have to create routing depay now i'm going to go ahead and go off of their documentation for now and create that so new file routing pi and we paste this in here so back into our settings we want to declare our a SGI application and i'm gonna put it right next to the root URL conf because it's very similar to our URLs because that's that's essentially how you doing this is the routing much like urls is routing this is routing for web sockets and worker processes all of that stuff so I'm just gonna go ahead and do CFE home obviously the name of my Jinga projects mod settings module configuration model and there we go so now that we've got that I can go ahead and run the server again and as you notice the actual run server is now using an a SGI development server it's no longer using the WSGI the difference is here is WSGI is like webserver gateway interface that's what it stands for a SGI stands for asynchronous web server gateway interface and since we have that we might as well do the production ready stuff for that and that is setting a SGI PI and actually bringing in a actual pretty much ready to go a SGI command that would allow our application to run so you absolutely need this when you go into production so you're no longer using the WSGI you're now using the a SGI module itself that is for production it's a little bit again we're trying to do a little bit of a shortcut here and getting things going as quickly as possible so then our wsgi application stuff we don't actually have to worry about that really right so that that's that's not something that is any of our concern anymore I mean we will keep it there but I don't actually think we need it any longer especially when we go live into production I'm pretty sure we don't actually need that any longer ok so we have the basics of our Django project or the Django channels project portion integrated so now it's just a matter of adding in some routes doing some consumers and talking about all that so stay with us [Music] so now what we need to do is create a consumer somewhere that our websocket can communicate with channels so think of consumers - channels like views are - Django so these are asynchronous and they can connect with our web sockets but they can also connect to worker processes but in this case we are just going to be using the WebSocket related stuff so let's go ahead and do it we're gonna first off import async IO which is a built-in Python library that allows for asynchronous code then we're gonna import JSON and then from Django contrib dot off we're gonna import the get user model method we're going to come back to that one later and then we're gonna also import from channels dot consumer import async consumer and do note that I have consumers inside of the chat application itself they don't necessarily have to live there much like views don't have to live in an app this is just a recommended method okay then the next thing the final thing that will import from third-party packages is from Jing or channels DB import database sink to a sink and then finally from da models import thread and chat message so if you haven't looked at it already definitely look at those models for those threads and those chat messages will basically be talking about how they work in a minute but or when we actually work through them but for now let's just focus on the consumer itself so we make a chat consumer as a class that inherits from the asynchronous consumer so I'm going to copy and paste some code here in just a moment but what I want to talk about is writing a function inside of a class so it's a method a class-based method and something like web socket underscore connect and what the parameters are are going to be self and event so what we're gonna do is we're gonna print out connected and the event cool so this is a synchronous method that runs in our thread as normal what we want to do is turn it into an asynchronous method so it actually can be run and other things can happen too so they kind of happen at the same time or one can wait on another one finishing and still do other things it's not necessarily stuck so if you're not familiar with async IO or asynchronous coding and Python this is a little tricky so just bear with me and run through it so we'll go ahead and do a sync that turns it into a synchronous code you just write out a sync in front of it and again this is Python 3.5 and up it's built in we just write async and import in async i/o ensures that async will work I believe in future versions of Jango or rather Python we won't have to do that necessarily okay so we've got our check consumer here with this one method called WebSocket connect so what this does is it allows us to have our WebSocket connect to this consumer and we also have a couple other methods that we're just going to bring in as well that is receive and disconnect so these are three different methods that a WebSocket has and it hopefully it makes sense we've got the connection we've got when it receives something so when it should respond to something and then finally when it disconnects because since it's not a view since it's not like you know one request coming through its multiple events that happen when it's connected so normally when you go to a view you've got a request coming through and then you can handle something with that request and return a response consumers or WebSockets and more specifically channels don't work that way I mean sometimes it works that way but a lot of times it doesn't like a WebSockets a good example of that because a WebSocket and that page can connect and disconnect at the same time like it could happen like so errors can happen on the page where it disconnects or your internet disconnects and then it needs to reconnect all of those things can happen with WebSockets it's really cool that it allows it to reconnect to it without the page reloading or refreshing or rerunning the server all of those things can absolutely happen with WebSockets and that's why there's multiple events so that means that we can handle all this different stuff this stuff will make a lot more sense once we actually bring it into JavaScript but for now we've got just this basic check consumer and since I've got that I want to bring it into my router I'll do it in just a moment [Music] so if we look at our Django URL patterns we see stuff like this right so we have path regular expression path those are obviously Django 2.0 and up related stuff but we can also use our old-school one from previous versions of Django so Django kampf dot URLs import URL you could also do that as well right so what's happening here is we are building URLs that are related to an HTTP request each one of those URLs is then consumed by the view we have to do something very very similar with our consumer so I said before consumers are two channels much like views are two Django so the consumer itself we have to actually create some routing for it now there's a lot of different layers to this in the sense that you can divert from what I'm about to do but for illustration purposes and to actually understand what's going on I recommend that you don't so stick with us and let's open up routing itself I've got these things imported if you don't have these imported just pause right now go through it we're gonna talk about these things so what it's doing here is I have the ability to write my URLs in here now I'm not actually overriding my regular HTTP methods I'm not doing anything with that all I'm doing is WebSockets and you may know WebSockets are a different protocol than HTTP so we can actually open up a WebSocket and a standard HTTP connection and have them work together that's that's completely ok and channels actually handles this for us and it does it really really well and that in part is because of this asynchronous code but it's also impart of how channels has been created it's it's really really good okay so now that I've got this protocol router what I need to do is I need to declare what this is so we're using WebSockets okay so WebSockets means that that's the method that I'm using and this is what it is named so stick with this value for the website gets if you try it with something different it might work but it also might not for sure WebSockets will so the next thing is we want to do a loud host origin validator so this right here is gonna wrap around our WebSockets and all it's doing is making sure that whatever host is doing the request or whatever domain name is doing the request will match the one that's written in here or the ones in this case I have all of them being accepted but much like with our URLs and cores we would not want to have other hosts that aren't ours or that we have allowed right and that's what this does it does it for our web sockets in particular this is in lieu of whatever is on our settings right so we can change that by using the origin validator so we can we can have specific origins for just channels by using the origin validator so use it look at the documentation for that but that's pretty cool the next thing is the auth middleware stack so do you want the user or the requested user to be inside of your web socket you want to be able to access that that user and my answer is pretty much always yes for web seconds in particular um in some cases you might not need to but for ours we absolutely need to and this wrapper allows that to happen so this class is allowing it to happen and what you see here is actually very similar to middleware and it's just middleware for the routing or that specific route it doesn't have to be on all of them but it is for that routing so it's just adding like I said that check that security check and then also allowing us to get what user is being requested this is very important the next thing is we use this URL router and then we put in a list here so what we have here is all of the channel relate stuff inside of this list it's actually going to be very similar to a standard URL so like this re path it's roughly that same thing except of course instead of re path we're gonna use URL right so the the configuration URL you could probably use already path that probably doesn't matter but as far as the documentation is concerned it has been URL just like this and then we want the actual path that we're gonna be using we'll take a look at that in a moment and then of course as our chat we want to use the check consumer and we don't do it as view we just write check consumer and that's it this is now our WebSocket URL but the challenge with this right here is that means that our WebSocket connection is going to be something like this our domain and then username okay and I don't actually want that I want it to be related or identical to how the routing for my actual chat message looks like so let's go ahead and comment that out for a moment I'm gonna save everything and let's make sure that we don't have any errors no errors okay and back into my project I logged into the admin and then I went to messages and then my user right so this is actually a forum and it actually does create messages that all of that works and this is all just standard Django stuff I could change it into Ajax if I wanted to but right now it's just a Django forum django views all of that stuff's handled it works just fine so if we look at the URL what I want to do is route my chat WebSocket URL to match my django view that's gonna render out the thread itself so it renders out all of my previous messages and that's actually I think really important for this is to make sure that we have the ability to match those things so I just want to change this to being messages in other words right just like that so messages and the username the username is the thread that I'm going to end up using and then I can also put a dollar sign you know close off the regular expression like that that last parts not unnecessary but now we actually have done our routing so we need to start working with our front in to make sense of all this to actually put it in two together because we've done most of the things that we need to do to at least start with what the front end does and we probably won't have to do a whole lot with the front end after the next video or the next portion stay with us so in this one we're gonna create our JavaScript web socket to connect to our back-end so the first thing that I want to do is just go ahead and say bar socket equals to a new web socket and we use capitals here okay so in here we're gonna have to put an endpoint so I'm just gonna go and say bar endpoint and I'll just leave it empty for just a moment okay so yeah if you know es6 you can absolutely use that syntax I'm just keeping things nice and simple and easy for the rest of us that aren't that familiar with JavaScript so just stick with me on this for those of you who are a little bit more advanced in JavaScript so there's a couple methods that would come in with this socket socket dawn message suck it open or rather on open sockets dot on error and then finally socket dot on closed okay so these different functions are events that happen for our web socket they are related to how our consumer handles any given event although these are the JavaScript side this is a client side and web sockets aren't only done in JavaScript you can do it in other languages or other front-end technologies as well and it would probably be very similar to this so the first thing is on message right so we want to say like once our socket receives a message that is our client-side web sockets it receives a message what do we do well I'm gonna just first of all just put in a function that would handle that and we'll just do consult log message and then E okay so I'm actually gonna copy this and put it on each one all right and change it to open error and close okay so let's save that and let's go ahead and look at our thread itself so if I jump into Chrome I see that I've got my servers running and I actually logged into the admin this is important because I didn't create off related stuff other than the admin so logged in the admin so I can actually see a thread I refresh in here and if I look at the cup of the JavaScript console with command option J I give this error WebSocket the URL is invalid so we can't have it an empty URL in other words this endpoint has to be something like we actually have to send it somewhere so the question is where right so what I have here is I have a few things that we could look at so let's do console log and all I'm going to look at here is the location so we'll just go ahead and do window dot location okay so I refresh here and I've got my window location so I have where it is right here we've got our path name ooh path name interesting and then I have my origin so these and all as well as my host so all of these things I can use to actually develop my web socket but WebSockets have a different protocol than HTTP so what I actually need to do is say var WS start as in WebSocket start or the beginning part of it and that's WS / / that's it so to actually make our endpoint we can do plus window dot location dot well if we look at what we console.log we see host so dot host that actually is now a WebSocket version of this URL but we still have to add in that loke or start a window dot location dot path name and this is actually where our WebSocket is so that's our actual endpoint but the thing the problem here is actually when we go into production so I always want to think about things in general if I can about production so that means that if we look at our protocol the protocol itself we see that it says HTTP now if we were in production and we were using a secure server which I always recommend you would see an S there so the WebSocket then would have to be WSS but if we're testing locally it's gonna be WS so we need to account for that and that's pretty simple all we need to do here is update our code just a little bit and say loke equals to window dot location and then if loke dot protocol equals equals to HTTP colon then we're going to change our WS start equals to WS s slash slash okay and then our end point is actually going to be WS start plus all that and we'll just put it underneath make sure our code is working correctly and then obviously I could change this to just being Lok or maybe maybe not so obviously but I'm there we go so we now have a way to connect to our web socket in point itself hopefully this code is simple enough for those of you who aren't that familiar with JavaScript so we save this and we refresh in here and I'm actually connected or at least I think that I'm connected so if i refresh it might take a second and there we go we actually get a WebSocket failed we get an error and it closes if we look at our terminal we see that it does try to connect but then it also disconnects cool so the WebSocket actually is connecting all of this stuff is communicating to each other except we still have more that we need to do on our back-end but now we see that the JavaScript connects to the backend the backend connects to the front-end they're just not doing it correctly so we still have to do that and that's something we'll do just stay with us [Music] so when we did our WebSocket so far only errors happened right it closed and it would say there's an error the error is actually the back end the back end has to connect to this WebSocket it has to accept it so let's go ahead and do that inside of our consumers we now have to send back a response to our web socket and we do this with all of the built-in methods so something that is self dot send and it's a dictionary and all we just do is pass in a type and the type is WebSocket dot accept so this would in theory send it but we actually need to use it as asynchronous code so that means we need to await it so doing a wait means that it's going to execute that code and wait for it to finish and that would be a wait so we save that now when we go back into our WebSocket we now see that it's open it's actually open and it's actually running really cool so what what is it that we want to see with our event right it says it's connected as we seen it didn't disconnect to just stayed connected that's pretty cool so how would I actually connect it and then disconnect it right so like what would I do for that well what I can do is I can use something like this we say oh wait and async I oh that's sleep and I'm gonna go ahead and sleep let's say I don't know 30 seconds now let's since we're recording a video let's do 10 seconds and then all I'll do is await dot self dot send and then let's try WebSocket close okay so it should connect 10 seconds later it's your clothes okay and again a weight so we refresh in here 1 2 3 4 5 6 7 isn't that fun hearing me count it's probably really lame okay so what it should probably do and there we go we now have it closed right nice and easy way of have any clothes from the back end so I opened it wait a little bit closed it so this means that my actual websocket consumer is doing all sorts of things but what I can also do is actually send something back to it so I did websocket clothes that just closed the connection but I can also do something like send oops send and I can send something so let's do some text and I'll just say hello world this time I'm gonna get rid of that oh wait method here we'll save that and actually that's why I import an async I oh I think I mentioned that it's because of these that's not correct it's just this okay so I saved that and we refresh in here and what do I see right there I see that there's a message and it says hello world pretty awesome that means that I can right off the bat I accept the connection and then I send a message right off the bat so this is where it's coming in it's going back and forth here there's still a lot more stuff that we need to do as far as actually sending this message and setting it up but the main thing here is seeing that I can write a few lines of code to accept something or even to send a general message so in the next one that's what we'll do we'll actually start to make this into a real consumer [Music] oftentimes when you use URLs you can grab the path of the URL and you can grab the requested user so all I'm gonna do is this one is show you those two things so the first one is we're gonna get the other user which would come from the actual URL path itself right so we just do self dot scope and this is gonna be URL route and then keyword arts and then well the user name so the keyword arg is coming from the route that's this right here literally that so if you change that name you would also want to change this so this would give me the other users username and then me would be well it's just self thought scope user and they can print those out so other user and me so we save that refresh in here and what do we see we've got Jay Mitchell 3 and CFE so Jay Mitchell 3 is the user I'm chatting with see if CFE is the user I'm logged in as so that gives me those two users awesome so now the question is how do we actually hook this into our database like how do we get the thread that's related to these two users which in our model we actually already have a method for that so this model itself will either create a new thread or it will get the original one so to do this we have to actually create a method on how we're going to execute getting this data so what I'm actually going to show you first is the standard synchronous method and that's this so we've got our get thread method here this right here will get the username our us me the user and the other username and then it's going to get that first value so whatever the object is right so either way it's gonna send back an object or that's what it should do so that's what the consumer is so this is get thread now this function itself is fine except now we're using asynchronous code so we have to use the decorator so we don't have memory leaks we don't have too many requests but we want to use the decorator database sink to a sink to actually grab what this get thread method is and then that way I can go ahead and print out what that is and all I do is thread obj equals two well self dot get thread and it's gonna be me and other user all right but and this is the big one is is this correct right so where are we actually getting the correct third and the answer is no you have to use a weight here because it's an asynchronous method so we have to use a wait for it to finish to actually get that thread object so now I can just go ahead and print out what that thread object is okay so we save it and we refresh in here the only reason I refresh is because I want to make sure that the print statements are coming out on that connect right so on I have to reconnect is the point that's why I'm refreshing WebSockets don't need that but I'm only refreshing because I have all these methods in this connect but the coolest thing is here we see that we actually have that thread object so that means it is working in the way that we wanted to so we were able to get the user we were able to get the URL parameter and then we were able to use the database with this one and again database sink to async is critical when you're grabbing stuff from the database using asynchronous otherwise you might overload your database open too many connections to your database there's a lot of things that could happen if you don't use that so make sure you do thanks for watching we'll see you next time [Music] so we've seen how our back-end can send a message to our front end of course we just console logged it at this point but what we now want to do is have our front-end send it to our back-end now you may or may not know this but the message by default already works so if I actually send a message there it does go to the database and it saves all that so that already does work so we actually don't want that action to be prevented if this socket doesn't open for some reason so what I mean by that is if we go into our thread and we look at socket on open this is actually where I'm gonna prevent the forum from doing its default action inside of this actual method itself the reason for that again is so the default action will still work so for some reason the sockets not connecting the default action will still work and all that would come through so that's what we want to do here now to do this we need to use some jQuery to actually reference this forum now I'm using jQuery you can absolutely use react or angular or just Rajah the script or pure JavaScript you can absolutely do that but you know for illustration purposes we're not focusing too much on JavaScript here so now what I'm going to do is just a bar form data or just the form itself is dollar sign ID form okay so hash form this will grab this form element here you don't have to know too much about jQuery here to understand that all you need to know is that the ID name is form so if I called the ID form data I would just change it like that so that actually grabs me that form and like I said I want to prevent the default action inside this socket so what I'm going to do is form data so that actual variable here that I just created dot submit we want to create another function and I'm going to call this one event to change it from the e so the event event dot prevent default okay so that just prevents my form from be submitted by default so now what I'd actually need to do is send this data using the socket back to my back-end now without doing too much stuff on the form itself let's just go ahead and just do socket dot send and whatever data that I want to send so let's just go ahead and say hello let's just put a basic string here at saying hello or hello world like we've been playing around with okay so all this means is I need to actually submit that form and then it will actually send something to my back-end and since we're on that let's look in our consumer and make sure that when we receive something we are printing those things out so when you send something you need to consume it in the form of receiving it we'll see what that does in just a second so I saved this and I refresh in here that's probably not necessary but I go ahead and run ABC I hit submit now the form doesn't actually clear out right because we prevented the default action from happening but what we see here is we actually received some data it actually came back to us so all we need to do now is adjust how that data is being sent from our thread all right so I sent some whatever data I didn't actually send data from the actual text or the measured message input itself okay so right now what I have is these this form and it's rendering out an actual field now there's a few ways on how I could go about grabbing that data but I'm going to just do it on a very simple way for specific reasons so what I'm going to do is I'm going to inspect the element on this message and I notice that Django renders out this form with an ID of message okay so Django renders out the idea of message so that also means that I can do VAR msg input' equals to dollar sign id id message okay so again we see id message here so we've rendered that out and now all I want to do is grab that data from my form so well above the send method here should maybe be last maybe not completely last but it should be up there I'll go ahead and save our msg text equals to msg input' bowel so this grabs the value of whatever that message text is and then finally I can send that message text back and then we can also clear out the form with just form data the zeroth element and dot reset so that just clears out the reset of the form so it resets the form for me I could probably also just use message input value is empty right I could probably do that as well so let's try out both of those things I'm gonna save that and refresh in here and say hello there buddy you know whatever I hit submit it clears out the form message as we see and it also sends out hello there buddy it also did send out hello there so I said hello there perhaps that was related to a previous item okay so we've sent that message through and it's actually running let's do that other one with message input value being empty just an empty string so ABC I hit submit that also empties it out so that of course we could play around with but the reason I would use something like message data or this reset portion is if I wanted to include other fields on this form and if or were to include other fields on this form let's just go ahead and say form data serialized equals to form beta dot serialize and let's see what that looks like so I'll just send that back to my back in okay so I do ABC I hit submit and we get oh I might have to refresh in here because I actually did change the HTML page so if I change the consumer I probably don't need to refresh but when I change the HTML or specifically the JavaScript I probably need to refresh so that that's why I did that okay so I type out ABC I hit submit and I look at my console and I see that I get two things in here I've got my text with the crs RF token middleware that's built into jรกnos features as you know and then also the message so this is not that ideal right I would probably just rather have the message itself and that's why I just used that message text there is other ways to get that data from that form but I'm just gonna stick with this one and then the other thing is I also want to just bring in this into as a dictionary I want to send a dictionary back just so you know how that works so I'll just call this final data and I'll put it equal to a dictionary and then we'll say message is that message text and just to send it back we would just do json dot stringify that data so you stringify the json data in order to send it you want to turn it into JSON data especially if it's a dictionary if it's a string it's no big deal but if it's a dictionary you absolutely need to do that so last one we'll say another exclamation mark hit submit and there we go there's a message so that means we'll have to do something with this and that's what we'll do in the next one so we come in here and we send a message we look in our terminal we see that our message coming through here that's great so what I want to do is actually work with that message so to do that I have to actually turn my event data and grab what's coming through there and the things that are coming through are just simply this dictionary right so if I just pasted that in I got my type and that's actually how this method knows about it right so when you call a consumer when you run things different messages or events the type automatically gets wrapped or mapped to the name of it right so the reason it's called WebSocket underscore received is because it maps to the type of WebSocket receive if you called it - it would not map there so it's pretty cool that it does that the next thing is we've got this text here and that's coming through and it is a Python dictionary right so this is actually a Python dictionary but in dictionary the text that front end text is coming through as a JSON string we did that on purpose we did json stringify that data that's on purpose so then on our back end we can actually use a dictionary value of multiple things instead of just a string of things that's that's that's the main thing I mean hopefully you know that okay so what I want to do is then grab this text and I'll say front text equals to event don't get and will grab the string of text or the key of text otherwise we'll say none if front text is not none meaning that key is actually in there then we'll go ahead and load it so we'll just call this loaded data equals to JSON loads and that front text of course I already have JSON imported here so there we go that should give us the dictionary itself so this is actually dict data right or a loaded dictionary data and then our message is finally I'll just say MSG equals to loaded dictionary data I get message and that will give us the actual message string and we print that out again it's not necessary if you want to just send strings themselves but if you want to send more data this is absolutely necessary like if you had other fields in there as well and I mean you might get in this habit anyway because if you want other fields at any time you could just quickly add it not have to change too much of the back end okay so we've got our printed message here it should now absolutely work so I do ABC hit submit and we should just see ABC here and we do so we received it and then we see ABC is working cool so now we have that message I can actually send it right back right so if I cut out this a weight part from the connection what you no longer need in fact I don't probably don't need these print statements anymore either cuz that should be pretty clear here now but all I'm gonna do is echo that message so this is an echo it now says if I go in here and say ABC hit submit it actually should echo that message right back to me so save it and refresh again and do ABC I hit submit and we've got that message event and here and we see that the data says ABC cool so it's a cooing it's working things are going in the direction we want to see well we still need to do though is save that message to our database which we'll discuss why in a minute but we also need to well make this work with that other user so if i refresh in here and just type out ABC and submit I actually don't see that message coming through so we actually do want that to come across on both of them that's how WebSockets are really useful as it gives that real-time communication to both users or any user that's connected to this WebSocket so that's something we still need you stay with us [Music] let's go ahead and display the message that actually comes through like from the data in the backend so what we're gonna do is we're gonna jump in to our thread dot HTML and we want to append every new chat item into this list now I'm not gonna worry about the styling I'm much more worried about the data moving that around so down here I'm just going to go ahead and save our the chats holder and we're gonna do dollar sign just like that so that actually gives me that element so I can pin things alright so I can just do chat holder dot append and some list element here with the message some sort of message and closing off that list element okay so how do we grab that message let's keep this commented out for a moment and we can console log e okay so we save that refresh and I just type out ABC I get e now it's actually at data so that should actually give us the message let's just use data in here okay so save that refresh ABC hit submit there we go it's starting to echo those messages there now this case I mean we actually don't need to worry about the echoing so much as well I mean I could already add this message up here alright so in many cases what I'd actually want to do is make sure that yeah I send that initial message so it's not it wasn't actually echoing so when I do ABC I should see it come up where it's ABC and ABC so it should come up twice but I actually don't see a user here the reason for that is back in our consumer we're not sending a user we're just sending basic text so that means that we have to do the same thing to the front end as we did to the back end in other words we want to send that JSON data back with a dictionary so to do this I'm gonna go ahead and say my final data or my response whatever so my response equals to well let's go ahead and just you mess H is msg and what else do I want here well I probably want the user name whatever user I'm using I want that user name and you might remember from before we use the scope here so that's the user that's getting this data which is me and then I'm just gonna go ahead and give a default user name of let's say default it's probably not great but I'm gonna go ahead and use it anyway we'll say if user is authenticated which it should be right but there's a chance that it's not depending on how you set this up there's a chance that it's not then we'll just go ahead and say user name equals to the user user name or rather me die user name but I'm gonna leave it as user so we passed that into my response and then of course like when we sent it from our front end we had to stringify the data we have to do the same thing but it's just slightly different in Python and that's JSON dumps my response so we now have a dictionary that's gonna go to the front in which we could console.log what that is or I could just go ahead and say var this is going to be the chat data or a chat data msg and that's gonna take in json dot parse EDA data so that's similar to Python or pythons version of that would be loads so that's the chat message data here and now I can do chat message dot msg plus you know like a space or something like that and then chat message dot username and that should give us that whole thing so I could say via that person and then when after refreshing here let's see it looks like I got an error I have an error in my argument list so let's take a look at what that is we have a syntax error oh yeah I need a plus there okay cool refreshing here and I'll say hello there exclamation mark one actually gives me the user and the other one it gives me undefined they spell something incorrect yes this should be message as we see here right so I actually logged that data already and it is message okay so refresh in here again ABC I hit submit there's the echo there's the original message right so like when I actually bring this on I probably want to get what the user is so I'm gonna go ahead and I'll probably just add it in here as an item so I'll do input type equals two hidden ID equals two my username and value equals to the user okay so the user that's being passed by default whatever that chat user is close that off and then I'll just go ahead and say bar me equals to my username okay and that means that down here I'll just say via me there go it's not refresh ABC I'm getting object an object of course I am because this should be dot username and this should be dot value cool again ABC hit submit I don't have any spaces let's go ahead and add those spaces but it shows us that hey this is the message I'm sending this is the message I'm receiving so again ABC and we can double check that by this response just changing it to this is a instant message and ABC there we go so the original one the one that I send goes first because that makes sense and then the one after go second so of course we could put timestamps and all that stuff but I'm not gonna worry about that because it's just not really that necessary at this point I mean it's it's over engineering this in my opinion okay so now what we need to do is actually connect these users together so they can actually send responses back to each other and it's not echoing what I sent we'll do that in the next one now in order for us to connect any given thread like this chat message here to another thread like this one so two different users even on the same computer or different computers in order for them to connect we have to have a data store of the events that are happening meaning when you know the user CFE is messaging Jay Mitchell 3 and vice versa we need to make sure that we know that those events happened all right so I have all these events happening but how do I make sure that the other one knows about it too and to do this we use something called Redis now Redis is part of the installation process so you might have already seen this but essentially what it is is a messaging cue for events that happen now don't get it confused with messaging queue as in the instant messages that we're sending or these chat messages that we're sending but think of it more in terms of the events that are happening and it's just a long list of all of those events and then what channel's does is can consume those events from Redis so it doesn't always have to happen just on your web socket but reddit's can also do some of the communication for us as well so luckily just installing Redis is well it's there's not a whole lot more that we need to do there after you install Redis now for a different like if you're on a Mac this will work if you're on Linux or Windows it's a little bit different so make sure you look up how to actually install Redis and if you're having a really tough time please let me know in the comments because I'll try and put together something for you if a lot of you can't find ways to install Redis on your local computer ok so now what I want to do is close out my project or my server and I'm going to do pip install Channel underscore Redis okay so this is a package that will connect channels to Redis you might already have it you might not but I want to make sure that I do I actually didn't have it so that's important part I can go back to running the server again the next thing I want to do is open up a new window here and just do Redis see Li ping I don't make sure that's going and also if I do write a server I want to make sure at least it's running or it says address and use which means that it's already there and we noticed that we have this web socket of six three seven nine I'm sorry not web socket but just standard TCP socket okay so that means that I can connect to it there okay so the next part is going into my settings file and I'm just gonna copy and paste stuff directly from the documentation all right so this channel layers configuration that's allowing me to configure Redis I just want to bring that in here and paste this okay so Redis server name in my case I can actually use a local host and that should be fine that should actually allow me to work with Redis itself now if you have any issues with this just look on how your system configures to your Redis server so your system might be different so for example in production something that you might have to do is this alright so if you are using Heroku for example this should work for you it might not but it should so I'll leave that there for a reference but this is how we use it on my computer it's on a Mac I think Linux and Windows might also be able to use this just fine it's just something I haven't tested ok so now that I've got this I can use more advanced things inside of my web socket and that is connecting various web sockets to each other or in other words making a chatroom right so we need to bank a chatroom and in order for us to make a chatroom we needed to integrate Redis so now we're ready to do the chatroom portion of this so we want to create a chatroom for these users and it's only a chatroom that's unique to these two users in other words we don't want other users to be able to get into this chatroom and we've already created the logic for that so if you go in to models not pi inside of the chat app you'll see this thread manager for the thread model and the logic here is simply that only two users can be in this model itself they are connected to one another and the only way we actually grab that thread for those two users is this method here so it's gonna look based off of either username and it's gonna go ahead and find that one single unique thread which means that in our consumer we can actually verify this by doing me and thread object ID so if i refresh on either one of these what I'll see is the username and the ID for that thread so obviously if I wanted to have multiple users in this thread I'd have to do something different but the point here is our chatroom names have to be unique and we already have a way to verify that they are unique by that thread object and the logic behind here should ensure that they will be only one object for any given to users it should ensure that if you guys find a better way to do this please let me know in the is of this project itself okay on the on github okay so we're gonna make our chatroom name based off of that the object ID so I'll say chat room equals to you know thread underscore and then maybe ID so we can use the format method for this or but yet I could use the F string if you're using Python 3.6 and above this is now our chat room name so I'll go ahead and say self chat room equals two chat room okay now what I also need to do is I need to use all this data before I actually connect to that WebSocket I didn't have to do that before but now I absolutely have to do that because I also want to do something called a weight self dot channel layer dot group underscore ad and this group itself will be the chatroom so we can say chatroom and self dot channel name so channel name is a default attribute of channels but the channel name comes out only if you have your channel layer set up like we've already done so back into our consumer this now creates that chatroom for us and we've added the current users channel to that unique name of that chatroom I mean this name doesn't have to be unique like you could have or it well it has to be unique in the sense that you can have all of your users in this one chatroom or you can have a unique per user or per user group like what we're doing here so it does have to be unique in that sense it doesn't have to be unique in the way it actually functions though but for this to work for us it absolutely has to be unique to these two users okay so now that we've got that chat room and I attached it on to the class itself for a reason we'll see that in just a moment but I've now added this group in here so what I need to do then is change how I do this response so this response has to be to my users in a different way it has to be to not just the actual WebSocket user or that one single user but instead what it has to be is to the entire group so we just do self that channel layer dot group send now and where are we going to send it and what are we gonna Center or what event are we going to send so all I'm going to say here is event equals to or rather new event equals to this okay so I'm gonna get rid of that we've got our new event here and I want to send it to my chatroom so self the chatroom is where we grab that created it when we connected now that we're connected we can use it and then literally just sent send through our new event which is this it has a type of WebSocket send and it's our response so let's try this out I'm gonna go ahead and save it and will refresh and both of them make sure they're connected open no errors are happening here that's a good sign so I do ABC I hit submit and we've got ABC via and so on okay so it actually didn't send the group like I would have hoped right so this might mean that I have to have a different method inside of this group send layer that then turnarounds turns around and actually sends out the WebSocket for us so what that means is I need to do a sync again so async define chat message and it's gonna take itself an event now check message itself is our thing we just made that up chat message is not related to the WebSocket it's just a custom method made name that I group made so you could call it chat message event or just simply chat message so in here what I'll actually do is I will send what this message would be so let's first off just call it and see what the message and event are okay so down here in this new event I'm gonna get rid of that for a moment and instead use my own new dictionary and say type oops type in a string like that and this is gonna be chat underscore message the same name of the method I just created and then we'll just go ahead and say message well let's just say hello world we hit save all right so now on our website receive we should be able to get something going on there I hit submit look back in my terminal I see that it says hello world and it says chat message so it looks like that message actually went through in other words this method is working so since this method is working what I want to do this message is actually gonna be this message it's going to be that original JSON message so again look at the key here let's call it text let's leave it in as text as we've seen other where in other places so now what I'll do is a weight self dot send okay and that's gonna be type WebSocket dot send and the text well what is the text it's related to the event that's calling it and this is the event that's calling it so it's simply event text so right here this actually executes the message so this sends the actual message and this in a way broadcasts the message and the message event to be sent so triggers this function for all of the members of that group in this case it's that chat room so I'm gonna get rid of some of these other comments here save that I could print it out and see what that works but let's go ahead and refresh in here looks like I might have a syntax error somewhere and I do line 52 952 oh this right here we need to get rid of that new event because we don't need anymore it's no longer a new event but we're still sending that response still going through but it's being triggered in a different place now so we save that we refresh on these pages hopefully they're running again and it is so I type out ABC I hit submit whoa what just happened well we get two messages coming through one says this is an instant message via CFE and this one says this is an instant message via CFE so that's absolutely something that's incorrect right so my response should be the original message or their original response which we called MSG here alright so I'm gonna get rid of this there we go and then the next thing is in my HTML I actually don't need to have my submit method doing the same thing alright so this right here I can actually get rid of because of how the messages work which will see if i refresh in here and say hello there buddy whatever I hit submit it's coming through here ah we might have to we actually have to refresh here because that was closed so let's try that again sorry about that and say hey it's MIT there we go so it's coming in to both places and it's coming in near-real-time we are really really close to being finished so stay with us [Music] there's two more things that I want to do to polish off this project one of them is actually storing this data so if i refresh in here I'm sure the messages are sent and they work right but as soon as i refresh either page it goes away so I want to make sure I do that the next thing is you know what happens when I restart the server it actually closes my WebSocket so I wanted to automatically reconnect luckily for us there is a third party package that does that for us automatically so that one's really simple what we do is I'm going to just come into thread dot HTML just put it in a script here script source equals 2 and I'm gonna just kind of copy and paste some code here I'll show you where you can get this in just a moment and then we put that script that item itself just a quick Google search for reconnecting WebSocket it will bring you here you can find that code you probably get the latest version of whatever it is the next thing is actually changing the WebSocket to reconnecting WebSocket and we bring back in here this part right there reconnecting WebSocket and that's it it now will reconnect for us so that doesn't mean that it's gonna stay connected like if the server goes down but what it does mean is if I close out the server it actually closes right and then I restarted the server hey what do you know it's back up that part's cool so that makes it a little bit easier than trying to figure out a way around reconnecting it just does it for us nice clean easy ok so the next part is actually saving this data but you should already pretty much know what to do and that's this right here right this is a hint for it so I'm gonna go ahead and paste that underneath here and just say create new chat message or rather just create check message and what its gonna be is we've got a user and probably our message so let's just say me and our message and then we look in our model to see how we actually create this well it's a very simple model itself all it needs by defaults to make this work is thread user and message so me as in the sender thread as in the thread object right and then message whatever that message is so we already have me and user in here let's make sure we have a chat message install or imported brother and just create and again it's going to be thread equals to thread obj wherever that is we'll figure that now second and then it's going to be user equaling to me and then message equals to msg you know obviously those are the parameters that are being passed well where's this thread object what we actually have it when we connect and as you see we can add these things to the class itself so we just do self dot thread object equals to the thread object cool there we go we now have our thread object here so thread out obj equals to self dot thread obj let's give us some space here so we can see that a little bit easier me well there's obviously a few different ways on how I could do that I could use it based off of the scope right so we don't actually have to pass it as a parameter let's not do that let's just go down here and use it in as the scope okay so get rid of that and the last thing all we need to do is the message itself and where do we get that message hey what do you know my response so it's a weight self dot create chat message just with the message again we could pass the user in perhaps you do perhaps you don't it's kind of up to you on how you want to go about doing that the only reason you would actually pass the user in maybe not the only reason but one of the main reasons is you know putting it inside of whether or not they're authenticated and that's the only way you would save that particular message to that user right so there's definitely things that you could go about doing this where perhaps you have your own model manager meth that would handle whether or not that user is authenticated that's that's getting outside the context of this and really just not that important it's important in the scheme of making it work correctly but it's not important in the scheme of how genuine channels works okay so if i refresh in here which I didn't actually have to do but I did it anyway the reason I didn't have to do it is because again we saved that consumer I saved it it closes it opens right back up reconnecting for the win and now I can say hi there I submit it well it looks like it closed and had to open back in up again so hi there we hit submit it seems like it's closing that's probably not a good sign actually so maybe reconnecting for the not win so let's leave it as just the WebSocket itself refresh in here and try that again seems like it's not working ah that's why me is not to find we've got a server error so let's leave that reconnecting back come back in here where is that server error oh it's right here so me obviously it's related to that I should have used user to create that chat message after I change the parameter of how it worked okay so now if I reconnect ABC pretty cool obviously this one's not connected but if i refresh in there ABC comes through because I saved that in the database and when we first go to this view it will reload all of those items so I say hello again exclamation mark it says hello again again i refresh on both of these pages those things are absolutely there and if I go into my thread object I think I have it in line there I see all of the messages in there as well and to the correct user right so CFE users logged in and I say what slash CFE user so all of that stuff is working correctly we now have a chat application that is ready to go into production there's not something we're going to cover at this time in this video it is absolutely 100% covered in our course for this so go to join CFE comm slash courses and get a much more in-depth look at chat and channels doing it going live doing WebSockets and then also doing worker processes worker processes are offloading tasks like let's say for instance you want to make an image thumbnail that's one thing or if you want to do broadcast messages across all of these messages like you are the system admin and you want to go to every chatroom you have that's another thing that you'll learn in there as well but I mean of course I want you to join that but I think that you got enough here that you can now explore in the documentation on your own to implement those things on your own that's how I did it I think you can do it now too of course if you want to save some time just check out that course love it if you did and make sure you subscribe to get everything I'm gonna be doing a lot more stuff on Django some stuff on react some some more stuff on JavaScript just all sorts of code stuff because I love software and I'm super excited to share with you guys on how to do all this thanks so much for watching and I look forward to seeing you next time [Music]
Info
Channel: CodingEntrepreneurs
Views: 141,321
Rating: undefined out of 5
Keywords: django 2.0 tutorial, django tutorial, install django on mac, install django with pip, install django with virtualenv, virtualenv, python django, Django Web Framework (Software), Mac OS (Operating System), Python (Software), web application development, learn django, installing django on mac, pip, python package, django, kickstarter funded, beginners tutorial, django channels, channels 2, real time, websockets, django real time
Id: RVH05S1qab8
Channel Id: undefined
Length: 72min 42sec (4362 seconds)
Published: Sat Jul 07 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.