Build a Django REST API with the Django Rest Framework. Complete Tutorial.

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello welcome to the django rest framework tutorial series [Music] in this one i'm going to take you step by step to build your very own rest api now why would you ever want to do this well if you want to make a tool for other developers building a rest api is probably one of the things you'll start with you'll actually learn how to make your website be able to communicate with other pieces of software whether that's a react.js application a vue.js application or another django project or a fast api project or really anything because once you have the structure of a rest api your software can communicate to all other software anywhere in the world and you can do so securely which is fantastic now we'll start with a pure django rest api because it's definitely possible then we'll see some of the downsides of that to introduce the django rest framework which actually really shortcuts how we can build our own rest api and it does so in a way that well i've been using for years then we're going to go ahead and build our own python client a client that will emulate if another developer was interacting with our service then we'll go ahead and create a javascript one so you can see how that's done even if it's a little complicated and then finally we're going to integrate with a third-party rest api service and it happens to be the sponsor of this series algolia now i actually use algolia on coding for entrepreneurs.com for search it is my default search engine as of today which is fantastic too i am really really enjoying their service but you'll see that algolia is a result of building a really powerful rest api itself and you could do that too and it starts right here with the jqrs framework now my name is justin mitchell and i'm going to be taking you through this one step by step of course if you have any questions please let me know otherwise let's jump right in all right so let's do a quick overview of the tools we'll be using first and foremost if you ever get stuck all of the code is right on github for your reference so check the link in the description for that or just navigate to cfe dot sh slash github and you'll find all of the repositories that we have next we're going to be using the django rest framework hopefully that's fairly obvious by the title of this series but the idea here is we want to go into this in a lot of depth it's not going to cover every aspect of the django rest framework because the documentation is incredible i think once you have a solid foundation you can go through the documentation really really easily and the generous framework is built on top of of course django right so you absolutely need django to run the django rest framework hopefully that's clear but if it's not hopefully it is now now the thing about django here is we can go in depth in a lot of ways with django i am not going to go in that much depth but i will cover some of the basics on at least getting our django rest framework stuff up working and why you might need the january framework and not just django that's i think a key part of all this too next of course we are going to be writing all of this stuff in python because django is written in python jkurus framework are written python so make sure that you have some experience with python check out my 30 days of python series if you do not really fundamental understanding of python is okay like if you can do functions you're probably okay if you can make classes you're probably good that also assumes that you know how to do variables and stuff like that next of course is we're going to be using python requests so when you design an api a rest api with something like the django rest framework one of the cool things that you're going to want to do is actually test that it's working i'm going to go into a lot of detail here this is not automated test this is like as if you were developing something to use that api again i'll go into this a lot more in this series python requests will allow for us to do that next of course we're going to be using the django cores headers this is really important in order for all of this stuff to work correctly and it's going to be a little bit more important once we get into some of the web-based component of this for the client itself again that might be a little complicated but i do want to set this up to make sure that your jures framework is ready to work with other applications pretty well and then of course i'm going to be using vs code as my text editor my code editor you can use any one that you'd like this is the one i'm going to use and i'm going to set up and stuff like that it's free it's open source so i highly recommend that you do that so if you have any questions on the tools and stuff like that let me know in the comments otherwise let's keep going now we're going to go ahead and set up a python virtual environment to isolate all of our project versions and whatnot and then we're going to go ahead and start our django project open up terminal or powershell depending on what system you're on and go ahead and type out make dirt dash p and then it's going to be in your user root which is this tilde line right here and then dev this will create a new folder called dev if it doesn't exist then we're going to go ahead and call our root project folder as drf you can call it whatever you want just be aware of what you end up calling it we hit enter here and now i'm going to go ahead and cd into that folder of course change directory here and if i do pwd on linux and mac i see the actual path if you're on windows you can do dir that will show you that same path as well notice it has my username in there and whatnot so i can also open this folder with simply open period on mac and linux if you're on windows it's just ii period and that will open that directory cool so now what i want to do inside of this route is i want to actually create a virtual environment now we do this by using python the version of python that we want to use in my case i'm using python 3.10 you need to use at minimum python 3.8 so python 3.10 dash mvemv venv now i'm actually going to be putting my virtual environments in a new folder called venv this is a bit better practice if you've seen what i've done in the past but anyways so once i do this this is going to contain all of the files and reference files third-party packages all of that stuff that's related directly to this particular project not to any other python project but this one in particular as you might already be well aware now if you're on windows the command is probably going to be something a little bit closer to python 310 python.exe dash mvemv vmv cool so once you actually have this venv folder or this virtual environment folder we need to activate it we need to do this every time we start working with our project so to do this on mac and linux it's source v e and v bin slash activate just like that now it's active as we see right here if you're on windows it's dot slash uh v e and v slash scripts slash activate and that will also activate it as well and it will look very similar we'll have this v env at the beginning of that now when i talked about actual differences in python versions the package versions that has to do with of course everything we install through pip so if i do a quick pip freeze i should have nothing installed in my case if i open up a new terminal window and do pip freeze on my main computer well i don't even have pip running here right so that is a sign that my virtual environment is working in some cases you might see other packages installed so let's actually install a lot of our packages before i do that though i'm going to go ahead and do code period what this does is open up vs code for our project if for some reason that didn't work just open up vs code you would see something like this and then navigate over here and hit open folder or just simply file open folder and then you can navigate to the folder you want to work in and so now that i've got vs code here i'm going to go ahead and save a workspace file so we'll go ahead and save workspace as and drf code workspace simple enough not that big of a deal so from going forward we're going to be using vs code for entire environment so i can actually toggle the terminal in here as well so whenever i open this up this is just control tilde okay so control tilde will toggle this for you and that's true on mac linux and windows that will toggle open up a terminal window of course that's identical to this terminal window it's just slightly different in the sense that it's inside of vs code and so if i actually run this now again i need to make sure that i run source venv bin activate to activate that virtual environment that is a key part of this okay so now that we've got all this all going we've got our virtual environment i'm now going to go ahead and add in a requirements file so requirements.txt now we're going to just add in all those packages that i mentioned in the tools so django django rest framework pi yaml we might end up using python requests and then django-cores headers okay so in my case for django what i want to do is i want to actually have a specific django version because django itself does evolve mostly over time more than all of those other packages in terms of how it actually works and how it functions with other things so what i want to do here is go into the django project and look for the current version in my case it's 4.0 now i always recommend that you should probably use the lts version which in this case is 3.2 but i'm going to go ahead and go with 4.0 and i'll just show you how easy it is to switch between those two versions what we're doing here won't have a major impact on the django versions so what i can do is say django equal greater than or equal to 4.0.0 and then less than 4.1.0 that's it right so 4.1 is the next version and this will actually install the latest version of django 4.0 when we come back into our virtual environment activated and run pip install r requirements txt this of course will install everything now i realize for some of you this is a lot of review but i always like improving best practices when it comes to my own requirements.txt and my approach for these virtual environments which is why you see slight changes from time to time i'm also going to go ahead and upgrade pip so pip install upgrade and pip and there we go so now with that going i'm also going to create two new folders in here so the first one is going to be called backend and then inside here i'm also going to do pi client so back in is where our django project is going to live and the django rest framework pi client is going to be something that consumes that back end which we'll see just in a little bit so inside of this back end here is actually where i want my django project to run so i'm going to cd into that back end and we'll go ahead and do django admin start project and i'm just going to call this cfe home and then i'll just put a period here so it creates it and inside of the backend folder and then i've got this whole project here now i actually recommend that you stick with me on calling your django project cfe home that way a lot of the paths and how i do things isn't going to be a whole lot different going forward so now that we've got some of these fundamental things done let's actually build our first python api client it's actually pretty simple as we'll see in a moment we're going to go and create our first python api client but before we do that i want to mention that there are new files in this project and that has to do with git that's git or version control same with this number down here so four dash end this is actually corresponding to this is the fourth section and once i actually finish this fourth section off the video i'll actually submit that code onto github so you can have a direct reference so if you go into these branches here you can see the state of the code at the end of that section and if you're ever confused what section it is just check the video it should have the correct section for which one you're going to want to reference okay cool so with that out of the way let's go ahead and create our first python client and so i'm going to go ahead and create a file in here called basic.pi and i'm going to go ahead and import requests and i want to set an endpoint now an endpoint in terms of a api client is really kind of like your url right you could call it a url but it's very often that you'll declare it as an endpoint because throughout your client you'll have several different endpoints so when you're working with a rest api there's going to be a lot of different endpoints let me just show you one for example so i'm just going to go and say https www.github.com so i can declare this as an endpoint so if i copied that and went into github.com on my browser i'll see what this data ends up doing let's actually look at it inside of a incognito window where i'm not logged in this is our github.com page right so what actually happened here was our web browser made what's called a request to the github servers at github.com and it returned what what did it return what did the browser return i i really hope that you already have a sense as to what it returned but if you don't that's okay we can take a look at something called view source so in the developer stuff we see view source it returns an html document that's it so the server is sending back a document the web browser reads that document and then makes it look cool basically so this is not an api endpoint at all a rest api endpoint at all it's really just going to return html so what we want to do is use an actual rest api endpoint don't worry i'll explain the api of it all in just a moment but i want to use an endpoint that's real to do that i'm going to go ahead and use a place called http ben.org so i'm going to go ahead and copy http bin.org and paste it into this endpoint so what we see here is a bunch of different potential items that i can use so if you click on any of these items you see that there is a path here so in other words if i go into hdbin let's just actually take a look at this path in our browser so if i go slash status slash 200 i actually am going to a path this is another endpoint right so i'm going to go ahead and put this one above and we'll actually look at that end point as well so that's pretty cool now this is why you call it endpoints because there's many different endpoints that a api a rest api will end up having so i'm going to use one called anything this endpoint will echo back what i send to it which we'll see in a moment so to actually request something here we need to use the library called requests we need to use the api that's in this library in other words i need to use something called request.git this is basically an api but it's a function it's an actual method that's built into it so api just stands for application programming interface so application programming interface okay so using this library is a form of using an api it's just not always thought of that way another thing way to think of an api is like your phone right so your phone has a camera and every app that uses that camera uses an api to grab that camera now why am i telling you about this and the library one that's because these are examples of not apis these are just library apis these are not rest apis so rest apis are what we're going to be using and what we're going to be building they have to do with the internet that's really more of like a web api that's kind of how you're going to want to think about this as a it's a web api it's certainly not the only kind of api that a web can have but it is the one that we're using now i'll go over rest itself later but for now just think of rest apis as a web-based api now that doesn't mean that it has to go across the internet but it does mean that it's going to use something called an http request now remember back when i said we're using python requests now the reason for that is because it does http requests for us so it actually does the kind of requests that we're trying to do with our apis now this might feel very complicated so let's go ahead and start looking at more practical things first off we're going to do using the request library we're going to use the get method here this is going to emulate a http get request and actually before i do anything that endpoint i'm going to go ahead and just grab the home page of heb bit which is what we've been on right so if i go back in here to just hdbin.org this is the home page okay so if i actually do this get request i should get a get response back right so this request right here will be assigned to the variable of get response and what i can do in here is do get response dot text this is printing out the source code or whatever is responded the raw text response the body response if you will so now if we save this let's go ahead and do a quick run here i'm going to go and run python and pi client slash basic dot pi i hit enter and what do i get back hey what do you know it's identical to the source code so if we go into view developer view source this is the same thing so what you may have seen already and something i might have even shown you on one of my videos is how to actually scrape data using python requests that is how to open a web page and grab the data that you may want now in our case that's not what we want to do we want to use an actual rest api and of course hdb bin has that so if i change this to simply anything just really just changing the endpoint to anything what's going to happen here now if i run it i actually get a different kind of response it's formatted data that my actual python application could in theory use right so that's the huge difference here on one hand a regular http request a non-api request will give you html a rest api request which is still http but it's a let's say it's a rest api http request that will send usually something back called json sometimes it's in xml and other formats but we're going to put it in json i'll explain json in a moment so this is really just the difference on one hand a web api allows your application to work with another application through the web through the internet some sort of internet request when it comes to an http request you get html that's made for the browser that's made for humans to look at rest apis isn't really made for humans to look at granted we can design software around them so humans sort of have to look at it but it's not really meant for humans it's meant for software to communicate with each other over the web okay so let's go ahead and take a look at this json thing here i'm gonna go and comment this out for a moment and what we got here is we can actually look at this text again and we see this data right here so if i copy this manually and paste it in here this is actually a valid python dictionary with the exception of this json null here it's almost a python dictionary but it's not it stands for javascript so java script object notation which is almost a python dictionary but it's not right they're just slightly different and that's because javascript object notation or json right some people call it json but to me jason is a buddy of mine json is an object type that's really how i think about it but you can call it whatever you want just know that that's what i do anyways so now that we've understand hopefully a little bit more trust me we are going to be working a lot with json we're going to be working a lot with all these things so even if it's still confusing hopefully over the course of this series it'll be a lot less confusing because we're going to create a lot more clients okay so anyways so we've got the potential to use json it's almost a dictionary now what we can do here is if it is json response we can actually run something like get response json and i can now actually print this out as a python dictionary now it is a proper dictionary so i can copy this raw code again and we can paste in here this time the json of it all is declared as none instead of null in this case that's the only difference there are of course a lot of differences that could occur but that's the main one so the other cool thing about the python request library is i can actually pass in my own json data so if i do json equals to again a dictionary i'm going to go ahead and say query and hello world okay so again if i'm going to be using json with respect to python i need to think of a python dictionary so i'm going to go ahead and save this and we'll go ahead and run this again and now what happens is http ben actually echoes back what i sent to it now it echoes it back in a way that might not be that familiar with you because it's giving you a bunch of other data but the general idea here is it is echoing back the data that i sent to it before it did not right so if i look at it before it just has empty data here now there are other ways to send data i don't have to send it as json i can send it as raw data and when i run that now it's actually in a different location it's now considered form data just slightly different but different enough and we see the content type being application http form url encoded again so changing it back to json this is going to show me a header that has changed to application json okay so there's a lot of details that maybe you don't think that you need yet and maybe you don't but the thing here is that we can actually play around with a bunch of different data types to interact with a rest api we can send form data we can send json data typically speaking though if it's going to receive json data you're going to send json data so what you send you typically receive that's kind of how it goes it's not always the case but it's something that you might consider so this has been a really long-winded way to write some incredibly basic code and an incredibly basic python client now what we want to think of in terms of clients is very similar to like your web browser client this is a way to interact with the internet when you create a client it's going to be a way for you to use python to interact with your rest api so in other words this pi client has nothing literally nothing to do with the jqrs framework and django or even python on the back end or whatever is running this endpoint this client does not care and that's another key thing about rest apis is they can be consumed across all kinds of different clients so in uh in other words if we actually look on httpbn.org anything and hit enter i get the exact same data or roughly speaking the exact same data that i did with my python project here you might see it as something a little bit more like this but this is the same response and if we look at the source of it as well you can see that it's also still the same response so i actually just showed you two different clients one is a client through python the other is a client through chrome or whatever web internet browser you have so you can have almost an unlimited amount of clients consuming a rest api as long as they can do these are really cool http requests okay so now we want to build on top of all this of course and we want to actually control what the client sees we want to control this endpoint we want to send out the data we want to send not the data that somebody else designed right what somebody else designed is incredibly helpful for us but it's not necessarily something that we want to do now before we go i want to mention one last thing and that's this endpoint here of status 200 what we can see though is there's also this thing called status codes so getresponse.status code we can also see what that is and if i run this again i'm getting a a 404 not found so instead of actually using this endpoint we'll just go ahead and comment this out for a moment to just stick with anything for now just so we can see this status code here if i run it again i see this number of 200 which of course if i comment out some of these print statements i will still see that number of 200. so this status code is something that you might not be that familiar with but it's something you definitely use all the time so in other words if you actually go to a page that does not exist this status code is 404 so there are a number of status codes that we'll need to take into account when we start building the back end and it's just things that you can already start playing about with and also learning about so check out the github or rather the wikipedia article on http status codes because it is pretty fascinating to learn more about it but again we will go over those things going forward okay cool so if you have any questions on this one let me know i realized we covered kind of a lot here but i really wanted to sound send a foundation for what it is that we're really trying to accomplish which is first off creating a way to consume an api and then creating a way that we can actually design the api we can actually just dictate what should be consumed or what can be consumed so that's what we'll do going forward so that last one i unloaded a lot of things on you so now we're going to go ahead and chip away at each little piece of that little by little to put it all into perspective so the first thing that i want to do is really just change this end point in my python client here which of course means that i want my django project to run so i'm going to go ahead and open up another terminal tab here you can do it either on the side here or you can just hit plus here and have two different tabs it's really up to you on how you want to go about doing that i'm going to have it on the side so i can always see that django's running i do this when i'm just testing things out locally you can as well so what happens here is i'm in two places one is going to be in the root of my actual entire project the next one is going to be in the root of my django project so i'll just cd into the backend here and i'm going to run the simple command let's go ahead and list those things out again the symbol command of python manage.py run server i'm going to also declare the port i want to use now if you have used django before you know this is the default port but i want to make it very clear that this is the port i'm using you can ignore all errors for now but that's the port that i want to use okay so what is key about this is this development server running at this url or this what is it called in point so i can actually copy this right here and i can change this endpoint in my api basic file or my client file and now i have a new api endpoint right now it's actually based off of django so i'm going to go ahead and do the error first so i'm going to stop the django server and i'm going to go ahead and run python pi client slash basic dot pi hit enter and i get this connection refused all this is saying is that python requests cannot access this this url that's it it just cannot access it it just does not exist and of course if i did the same thing in the browser so if you hit command period or control period you'll see this too site cannot be reached the browser does the same thing hey what do you know error connection refused it's the same error okay i only wanted to show you that so that i can then run the server and i can go ahead and modify things a little bit inside of this basic code and that is getting rid of this json stuff i don't have those things available yet i do have the status code though so now i should be able to print out the raw text as well as the status code on this endpoint i'm not even going to open it on my browser at this point i'll just go ahead and run my basic client again and now what do you know i got html coming through and if i scroll up a bit it says getting started with django tutorial all sorts of cool stuff that are related to our django project and of course if i go into my web browser now i see this here pretty cool so for a lot of us using this url 127.0.0.1 is okay i'm actually going to go ahead and go off of a local host if this works for you stick with localhost i actually prefer writing localhost for a number of reasons but one of them being that it's a little bit more practical in the long run for all kinds of systems and also even in production um but we'll get there some other time for now just go ahead and use your local host or whatever you know whatever works either one just make sure that one of them works and you can actually use it okay cool so the key thing here though is the port we want to make sure that we always have that port available whatever that port ends up being we want to make note of it when we're working locally otherwise your endpoint is going to be incorrect so in other words if i change this port to 8002 and try to run it again i still get that connection failed okay cool so that's it that's it for this one i just wanted to show you how simple it is to do a request through our python client to our django project we're that much closer to already having our first api now let's actually create that first api at this point hopefully it's not incredibly surprising that when we run our python client to this endpoint on django what we get back is well html we get back exactly what this web page is showing us so what i want to do now is actually create my first api view to actually give me json back hey so we can do this stuff again okay so to do this i'm going to go ahead and close out the running django server with control c and then i'll list out make sure i'm in manage.pi i'm going to run python manage.pi start app and i'm going to just call it api so typically speaking when you create a new app you're going to want to come into settings.pi and go into the installed apps here and just add it in as api again i haven't done a whole lot with django just yet the only real thing i did was start it and then add this new app called api so we'll leave it just like that for now next up inside of the api project or the api app itself inside of django we're going to go into views.pi okay so here we're going to design our very first api view our api endpoint view so i'm going to go and get rid of everything that's in here and just run from django.http import jsonresponse notice that the son is lowercased hey what do you know json response what the heck i want json json responds hey it's built into django isn't that cool now i'm going to define a function based view and i'll just call this api home and it'll take in a request we'll let it pass in args and keyword args even though those are probably not necessary for us at this point then i'm going to go ahead and return a json response and let's spell it correctly and so notice that i actually did not capitalize it correctly i spelled it correctly but just didn't capitalize it correctly so let's go ahead and do that and now i'll go ahead and return with a message saying hi there this is your django api response okay cool so now that we have this view what i'm also going to do is inside of this api i'm going to go ahead and create urls.pi now you certainly do not have to go this method the reason i'm going this method is to just simplify where all of my django projects api urls are not everyone does this some people actually bring it right into the root django project urls i'm just going to have it separated out because i'm assuming that you're using other parts of your django application and you're not just building an api with django which you totally could let's go ahead and create the urls here so we're going from django.urls we're going to import the path function and then we'll do from.views or rather from dot we'll import views as a way to make it a little bit easier to grab all of our different views in here and then i'll do url patterns equals to a empty list here and we'll first off declare path and it's just going to be an empty path and we'll do views dot api home okay so of course that's this view right here and by all means if you like doing an explicit import which i typically do you would do something like that instead those are those are roughly the same things and then you would just use the function itself so now that we've got that url i need to bring this url into my primary urls file this is for all of django right and so what i need to do then is inside of this import i just need to import include as well and now create a path for my api so api slash this path is going to be like local host whatever my port is in my case it's 8 000 api that's going to be this path and then if i run include and then api.url this is going to be going off of this package right here or this module this python module and then inside of here we've got dot urls and that's what that is referencing and then it maps to all of the urls in here which this means that this is going to be also localhost 8000 api and that's it which is great okay so we will definitely play around with these urls a lot more going forward i just want to set up something basic okay so let's go ahead and run our project again our python manage.py run server and i'll again declare 8000 just to be explicit about the port that i want and so now that i've got that port going i'm going to go back into basic.pi and i'm going to go ahead and add in slash api that new url path that we created both in terms of the main django urls as well as the api app urls these are now corresponding to each other and so in basic.pi with this i can actually go ahead and run let's just run it like this actually just pretend like it's json right off the bat run it and what do you know it prints out not only the raw text from here it prints out the status code and then it prints out that json and we could go even further and say something like message and then i can go ahead and run that and boom you now have a django api endpoint it's really just that simple i really like it but one of the biggest problems with this at this time is merely the fact that well our view is raw data i want different kind of data here i do not want this raw data i want data that comes directly from the database so let's go ahead and start that now all right so now what we're going to do is have this view actually echo back some of the data that our pi client is sending or really any client for that matter and it's going to emulate to some degree what http.org anything does where it was echoing things back to us so the main thing here is i want to actually be able to see how to grab this data on the django side itself as well as another piece of data called params and i'll explain this in a moment and we'll pass both of these things in so we can see what's going on with this request directly then of course this response itself i'm going to go ahead and get rid of any sort of dictionary lookup there and just get the entire raw json and i'll also get rid of these other print statements too okay so back into our view how do i actually get all of these things out so the first thing is going to be that json data which is actually from request dot body now i want you to know that this request that's being passed through that is a http request instance from django right it has nothing to do with python requests even though it sort of looks like it you have an s there it's completely different thing you keep it in with just one single value request is an instance of the http request class which you can see in the documentation there's a lot of things that you can look at there or you could also print out the dur of request that would also give you stuff like request.body so anyways let's go ahead and say body equals through request.body here and so what i'm going to assume is this body is going to be json data but not quite json data just yet it is actually going to be a byte string of json data we can verify this by printing out that body data right so i've got some json data in there let's go ahead and just run the client and what we see here is it is a byte string of a json string essentially so a stringified version of a json object that might be a little confusing but the general idea here is i see that there is a string essentially around a dictionary this is always a good hint to me when i'm working in python that what's in here is most likely json itself so to convert it from this string into a actual python dictionary we have to import the json package and what we can do here is i'll just call this my data being a empty dictionary i'll go ahead and do try data equals to json.loads and there's going to be that body data and then i'll just do accept and pass for the moment and then we'll just go ahead and print out what that data is and more specifically i want to actually print out maybe the keys that are in here because it's a dictionary itself so i'll go ahead and do data.keys right this will signify of course that it is now a python dictionary and what we see here is it's actually doing that for me okay cool so it prints out the keys and now of course i can actually see that data now this means that i can use this data in all sorts of ways which of course we will talk about soon but for now i can actually change my json response to just returning back this you know dictionary itself because this right here takes in a string of json data and turns it into a python dictionary usually right which is kind of cool and the only reason i put in this try block is because it's certainly possible that your body does not have any json data so like in the case of this what if i came back here and just closed that off like that and then ran it well i shouldn't have an error on my server i should just return back with you know an empty dictionary of some kind but i can actually add to this data okay so the things that i might add are perhaps i want to add in the headers so i can say data headers equals to well request dot headers now in the years past older versions of django you would do request dot meta and then get all of the headers from that argument which is still in the documentation but newer versions of django use request.headers and it's well a lot nicer we can also do data.content type which actually should be in the request headers but we can access that directly so we can do request.content type as well right and there's definitely other things that i could continue to add to this looks like i have um oops i did context type that should be content type try that again and so and we've got http headers is not json serial okay so i'm going to leave this out for just a moment and we'll explain that in a moment i'll go ahead and print out what those headers are to see why so when i run this i get content type is application json as well as the original data that i sent through at least some of it okay so the reason that the headers were not json serial-able which really just means that it can't turn that header's dictionary back into json which is this right here so there's probably a number of things that i could do to ensure that i can change it back but i don't really need to echo it perfectly back i mean potentially you do but maybe not so another thing to think about here is you if you did json.dumps request.headers you'd probably see that same error right so let's save that again and run it again and yes see i get that same error so http headers cannot do that so perhaps if i change it into a dictionary itself that might solve the problem and let's try that again and sure enough it does so let's go ahead and now use this as just simply converting it into a dictionary and let's run that again and so now i get that those headers coming back cool um so the other part of this is perhaps that last data point that we want to echo which is the params here now these params are query parameters so whenever you see on a url you know question mark this arg equals to this value those are query parameters this is the key this is the value so what i have here is actually abc123 now you can set that directly on an endpoint and it will work inside of python request or you can use this param value to send it so if we save that and run this again naturally i don't actually have any things coming through but if i actually come back into my view i can print out git so what this does is this will get me my url query parameters always right so it doesn't matter where you are in a django view as long as you have access to the request object itself you can use request.get within that same breath we can also use request.post we'll talk about that when we get to the post portion because i'm currently not doing post methods i'm just doing get methods but if we run this again we can see here is that query parameter right so that's what's coming back from those params so what i can do then is say params or rather data and params equals to the dictionary of request dot get okay yet again let's try it again and see what happens and we get our parameters coming back just like that and of course if we take those parameters out let's take a look at what that looks like i run that and now it's just empty parameters so this echoing is now a lot closer to what i have with this httpband.org now why is it that i'm talking about all of these things well it highlights a number of items that we want to know about on any given view in django but more specifically for using json responses right so we had an issue right here where this couldn't automatically be converted into json data so we have to take account for that if we're doing it manually like we are and the way we take it account is just to enforce a dictionary value from these items right so whatever it ends up being it becomes a dictionary value now these kids still fail potentially so it is something we would want to keep track of because of the data types right so json and python javascript and python they don't all perfectly interact with each other json does a really good job at it as we've seen but there are still some downsides to at least this method of echoing and stuff like that and just returning json data so now the big question is how do we do something not a whole lot different than this but instead using you know actual django models to accomplish the response no longer echoing back just what the request is because realistically what your request is going to end up doing is maybe for example you would say our post id or let's say let's do something more like our product id is equal to some sort of product id that we will actually want to look up in the database and that's okay in terms of how this is structured this would actually end up working you know perhaps you would have it as a ul parameter or perhaps it would be in the url itself there's a like i said there's a number of different ways on how we can actually pass that data but generally speaking if we send some data in like this on the django side we now see roughly speaking how we can grab out that data and attempt to then do a lookup in our database so i think that's pretty cool and the reason we also want this fundamental understanding is because having this sort of building block will definitely carry over once we start using the django rest framework now we're going to go ahead and create a django model and respond on our api home view with an instance of that model so let's go ahead and close out our django server here and just run python manage.py start app and we'll call it products okay so inside of cfe home we'll go into settings and we'll add products in there and then inside of products we'll go ahead and add in a model this model is going to be really simple it's just simply going to be class product model style model and it's going to have three fields that's going to be title content and price now of course if you're familiar with django models title is often a character field or a char field with a max length of some kind i'm going to do 120 and then my content is often a text field in this case i'll just allow it to be blank and null and then finally my price is going to be a decimal field and we'll give a max digits of 15 and then a decimal places of two and then default being 99.99 okay very simple model very simple application so now what we're going to do finally is run our migrations for an entire project so we'll go ahead and do python manage.py make migrations and then python manage.py migrate if you're not aware make migrations that command just basically tells django hey we need to let the database know about everything that's happening in models.pi and then migrate just make sure that the database does actually change based off of what's happening in models.pi among other things okay so now we've got those migrations done we can actually jump into the python shell the django shell so python manage.py shell and then we'll go ahead and do from products.models we're going to import that product and then we'll go ahead and do product.objects dot and title being hello world content being this is amazing exclamation mark and then price we'll go ahead and do 0.00 and there we go so i can do the same thing over again with maybe a different product title or something like that and say hello world again there we go so now i have two products so one of the ways that we can get a random product is by doing product.objects.all order by with a question mark in here and then just saying first so every time it's going to roughly speaking give me a different model which we see by the numbers changing so those of course are the instance id or the actual model object id so the thing about this is if you're really confused as to what's going on here check out my try django 3.2 series because that will cover a lot more of the basics behind django and really give you a really solid understanding of this but just generally speaking it's pretty simple right so we've got a product model and then title content and price those are fields so if you think of it in terms of like a spreadsheet of course a database it's just fields in that and then each row is a new instance of that field or that whole entire thing okay so with this in mind let's go ahead and jump into the api and actually respond back with some of this data okay so what i want to do is i want to actually get rid of everything that's in here currently and i want to just highlight what it is that i'm trying to do here all i'm trying to do is i want to get my model data and there's going to just be random data to start so it's going to be product.objects.all again that order by and question mark and then first all this does is makes a random query set and then just grabs one of those values and this will give me my model instance so of course i need to import this product so we'll go ahead and do from the products the models import product now we've got that class imported so i can actually do this lookup so once i have this data i'm going to just go ahead and declare data as an empty string right that's it then i also want to say if model data because it's certainly possible that if you don't have any products you won't have model data so if the model data is there i'll go ahead and say data and title is equal to the modeldata.title and we can think about this for each part right so modeldata.content and then data and price and hopefully already you're like wow this is tedious okay so that's kind of the point because i want it to look tedious because it is tedious to do it this way but now that we've got this if of course we go back into our python client the json data is still coming through we will definitely address that but at least it's going to print out that response so let's save everything and let's go ahead and run that client now right so i'm going to hit enter i got connection refused oh of course i need to run my server let's go ahead and exit out to the python shell and then let's go ahead and run our server with our porch declared so we remember what it is up and that's odd oh i didn't spell manage correctly hey that's fun okay so manage.pi run server 8000 and there we go okay so now we've got that running let's try that client again it's giving us a error and another little query error they should say objects not attribute object okay so let's try it again third time's a charm so it is and there we go so now we actually will get sort of random data that's coming through here it doesn't seem that random but it is and of course if i wanted to add in the id of these things which is of course in a model by default unless you change it right so model.id that field of id comes in by default from models.model so we save that and i run it again now i can actually see the id for these objects and hopefully what's starting to take shape in your mind at least a little bit is how we will eventually be able to change this to being an actual id to actually do a lookup to our end part right to actually our api endpoint and so this is actually not great right here this is something i want to change now what's happening here is sort of the process of called serialization where we want to take a representation in this case it's going to be a instance so we've got an instance so let's say a model instance aka this we want to turn it into a python dictionary and then we want to basically return json to my client right that's essentially what i'm trying to do here so what's happening here is incredibly common to basically take a model instance and turn it into a python dictionary so that's actually something that we want to do for sure but i'm also going to make this a little bit more manual than it's been i'm actually going to go back a step on the json response to try a different kind of response as we'll see so let's go ahead and take a look at that now now we're going to convert a django model instance to a dictionary with a really simple built-in method so what we're going to do here is we're going to go from django.forms.models we're going to import the method called model underscore to underscore dict so this is most likely used in django forms a topic that is really interesting just not something i'm going to really be covering in this one because we're going to be using the django rest framework for the vast majority of things but this model to dict is really cool because now what i can do is just say data equals to model to dict of that data and we can save that and then i can go ahead and run this and what do you know that same exact stuff is coming back the other part that's really cool about this is then i can declare the fields that i want to add in let's say for instance id and just title i don't want anything else right i can save that and then run that and what do i get id and title this is great and also if i do id and price or let's do id title and price no contents right i can now specify the exact fields that i want this api to respond with now of course this is working towards the basic product like this right here maybe we will be a little bit more specific as to how that comes through but the other thing that it's also showing me is that this right here is a really clean and easy way to just narrow down data from a model instance but what i want to show you is if you weren't using json response if you're just like oh i want to do this the absolute hard way and see what that looks like by using something called http response now the difference between these two is json response accepts a dictionary as an argument http response accepts well it's supposed to be a string so let's leave it just in as that and i'll run the client naturally i get a json error so if i go back into my basic.pi i'm trying to print out json here so python request is like hey this is json data convert it into json data and use it in my case it's not json data it's well we can look so i'm going to go ahead and print out the get response headers as well and we'll just take a look at what's actually coming back so the headers are coming through here and what we've got is content type text html so going back into our view the http response by default the content type is going to be text slash html that's what it does by default so to change it to json response we can change the content type so let's go into our headers and say content and type being application and json we can save that run this again and it's now saying it's application json let's see if we can actually render out that json now so we've got the correct headers in here so i'm going to go ahead and uncomment these print statements and now i'm going to go ahead and try and render out the json data that quote unquote is coming through still get an error i got this id title price thing well this of course is because what i'm actually sending back is a dictionary it's this dictionary right here trust me i'm working out to something you'll see so what i need to do from this dictionary is i need to turn it into a json string so the json data string would be json.dumps of data and then we would return that data string that json data string i run this now i get object of type decimal is not json serial now this has to do with this price here this price field is a decimal field and if we print out this data and run it again and then i'll scroll up before the error what we see is a actual decimal place right here that's not great so it's actually not converting it completely in here and so here in lies one of the issues with trying to do all of this stuff manually yourself like trying to really peel back the onion to figure out how everything works inside of django trust me i love doing that but i also want to highlight that this is where django rest framework really can come in and just clean things up for us in a number of ways as we'll see so this json response doesn't need me to dump the data into json anymore it'll convert it for me now sure i could convert it most likely by doing dict equals you know data equals addict this should actually maybe solve that problem for me but even that didn't actually work so i'd actually have to change the price itself or update how it's being serialized through json again not something i want to do right now by all means go ahead and do it the main thing about this was this model to dict here so because i'm not actually going to do any of that right i'm just going to return back the json response of that data okay so we save that and now it's actually working as intended because it does all of the heavy lifting for us with just a simple json response here so the other part of this too is to realize that django itself is written of a bunch of different python code and if you know how to use it correctly you can really you know augment how quick you can spin things up and to me that's where the django rest framework also shines it just really makes things a lot faster now all of the things that we've been doing so far have to do with getting data like actually just looking up but it has nothing to do with sending in data that opens a whole other can of worms that we might discover a little bit more of with a pure django product here but what i actually want to do is start thinking in terms of this stuff and converting it into the django rest framework so that's what we'll start doing in the next part now we're going to go ahead and convert our api home view into a django rest framework view it's actually pretty easy to change but it does take two new items here the first one we're going to go ahead and import from rest underscore framework dot response we're going to import the response class here this response class will take over the json response class i'll get rid of these comments here the other part of this is i need to bring in the decorator so well we have a few options let's go ahead and take a look we'll do from rest framework dot decorators we're going to import the api view decorator and then we'll just wrap this on our api home view function so this is now a django rest framework api view right so this is a drf api view all of a sudden because of this mostly because of this and in part because of this okay so let's go ahead and save it let's run our client again and now we're getting a api views missing a list of allowed http methods okay so the cool thing about this is we now have to declare what methods we want to allow by default from any given api request so let's say for instance we just wanted git once i do that it now will require only git what if i did a post method only post methods here then i run this again now i get method git is not allowed so it's already doing some basic permissions on here now of course if i were to do this a little bit differently if i wanted to do it sort of the hard way without the rest framework so if i was doing back to django stuff i absolutely can say if request.method you know is not equal to git or let's say post in this case just because it's simpler based on our current client i can then return a response so we can stick with the same response here and i can say detail and get not allowed and then i do a status of something like 400 so changing the status code to 400 which i believe is what oh we said 405 here so we'll change this to 405. instead of 400 just based off of what i saw right here so that's the status code right there okay cool so let's go ahead and run that again and now it's essentially the same thing but i had to write out a bunch of stuff to get there including the status code right and i don't have to want to have to remember all the status codes all the time so wouldn't it be better if django rest framework just did it for me and that's exactly what's happening here now something that's also baked into this that i haven't done yet which we will get into is authentication related items doing a pure django view with an api much like what we have here and authentication is well not a easy task to just spin up and do it's it's non-trivial so that's another really cool thing about the rest framework as we'll see but now we have the same view roughly speaking and we have a way to just you know use the rest framework but i do want to take this another step further and that is instead of using model 2 dict i actually want to use a built-in serializer or a custom serializer within the rest framework itself so let's take a look at that in a moment i'm going to introduce model serializers with the rest framework but i want to set the stage for it first off if i created a property on this product something like sale price and i want it based off of anything right you could even call it a discount whatever let's say for instance it's going to be based off of the price itself i'm going to go ahead and do a float substitution here with percent.2f so i have two decimal places and then percent and this is going to be float of object.price times 0.8 okay and not object.price but rather self.price okay so now i've got this sale price property here now if i jump into the python shell we should be able to get that no problem right so from products.models import product and product dot objects dot first and then sale price hit enter and let's actually do dot last and now i got 960 okay and of course the actual price i think is twelve dollars and so eighty percent of twelve dollars which should be about 960. um roughly speaking especially how flow calculations are nevertheless we have this property now so what if i wanted to go back into my view into this model dick thing here and i wanted to add that in i want to say price or rather sale price save it exit out of the shell run the server again and then run python and pi client basic dot pi hey where's the sale price now naturally i could add a key to this from the model data and do all sorts of stuff to make it work but it's not showing up by default that is one of many features as to why i want to use rest frameworks serializers so if we go into products here and create a new file called serializers dot pi i like doing it plural similar to like if i was creating a model form i would call that forms.pi and actually let's look at a model form for those of you who may or may not be familiar but django has a entire topic that we can spend a lot of time on called model forms so it's very similar to this we do class forms and then we would import the model so from the models import the product and then we'd say class product form and it's forms dot model form and then we do class meta and the model is related to the product the fields that we want to include here are the fields we want to include let's say title content and price so that's a model form right now i go to depth on model forms and many other places but we're not doing that here i just wanted to show you this because of how similar serializers are or modest model serializers are to model forms so now in serializers.pi instead of importing forms we'll go from rest framework we're going to import serializers and of course instead of forms dot model form it's serializers dot serializer model serializer and then typically speaking you would call it your product serializer okay roughly the exact same as model forms and there are a lot of overlapping features that they have that will uncover as we keep going but now inside of my fields here what i can do is write out sale price this of course is a reference to this property of this model it's pretty cool so it could be an instance method it could be a property when i say an instance method i could say something like get discount and this could do some crazy calculation in this case i'll just return back i don't know 122. it means nothing in this context but um if i put that in here too that in theory will be interesting to see what happens nevertheless i now have this product serializer so what i'm going to do is into my api into views here i'm going to import it so from products.serializers we're going to import our product serializer model data i'm now going to actually call instance and i'll break down the explorer as i work on this this is definitely how you're going to want to call it going forward just leave it as as instance i'll comment out the model to ditch stuff for a moment and just go off of this product serializer so we go data equals to product serializer of that instance and then we just have to call the method of data after that so this makes it a class of that instance and then data is the actual data that's coming through from that instance we'll see that a lot more in the future too so now if i run this basic here look at that i've got my title i've got my content sale price price get discount great so one of the things that you might be thinking about is i don't want it to say get discount on my serializer i want to say discount or whatever right so if i save it as discount and run this again of course i get an error because there's nothing called discount right so if we look at our model there's git discount but there's no other field called discount okay so how could i actually implement this this is what's cool about model serializers or serializers in general on rest framework because i can enrich them with other values so what i can say here is say discount equals to so this is now that field name that was missing it's going to be equal to serializers.serializer method field and we can pass in something called read only being true so we save that and now i run this and still getting the error okay so it's not finding the get discount error right so saying has no attribute get discount so it's actually looking for another method called this git discount to make things less confusing i'm going to go ahead and go back into my serializer and say we'll just call this my discount so we can see what this error is now okay so we save that so we run it again now it's saying that it has no attribute git my discount so what we need to do is we need to find get my discount of self and object and what this can return is object.getdiscount cool super long-winded way to get there but the nice thing is check it out now my discount is the name of it it is no longer uh get discount now there are other methods on on accomplishing this same thing but what i wanted to show you was just a way to peer in to one of the many reasons why you would even use the django rest framework and model serializer might be the reason i mean there are other things that are really good about the django rest framework but look at how simple it made my json data that my client can now grab right so the other part of this that i wanted to highlight is the fact that i can get the actual instance so this obj here is the instance that's being called right so whatever that instance is whether it's we can actually print out obj.id to even see that instance right so if i run this again i see the id of one if i run it again and again those ids will rotate based off of that query set that i had before so the cool thing about this too is if i had obj di user i could grab the user.username and so on if i had a foreign key relationship like obj.category i could grab that stuff as well right now of course i don't have those things just yet and that will be stuff that we'll look at but the main thing here is the model serializer does a couple things really well to reiterate number one it will just do the model to dick stuff for us roughly the same way it serializes that data in a really nice and clean way and then number two we can start to enrich the actual serializer the data that's coming through in a very clean way as well now obviously or maybe not so obviously i can augment these values with actual inline fields as well we'll take a look at that as we go forward too but just a general idea here is the serializer itself has a lot of implications for how we can really change the representation of our data in any given view and we can do it with a number of serializers right so for instance if we wanted to have this as our primary product serializer we could do that we could say primary and then our secondary one now in this case they're exactly the same and i'm not actually going to save this but you can totally have multiple serializers for the exact same model is really the point there in cases that you might need that now this is where you're going to experiment in your own projects you're going to start to make a clear on what you'll need from these various serializers and you know realistically in this series we're going to only scratch the surface of all that is possible with all of the variations that you can do with serializers but hopefully what this does is highlight and maybe make you a little bit excited about using these serializers one thing that is so cool that we haven't done yet though is serializers very similar to forms can actually ingest data it can take data in and clean it it can ensure that this data is correct and right and so that's definitely another thing that we'll need to do in other words we're going to need to start using the post method here we need to send data to our backend not just get data from our backend so naturally that's something we will work on as we go forward if you were to go in your browser to this api endpoint you're going to see template does not exist and that might be a little strange the only reason for this is because we didn't actually follow the installation steps for the django rest framework we just installed this way long ago and didn't actually add it into the installed apps like you should have so i'm going to go ahead and do that now jump into settings and then just add in the rest framework here and save it and then if you go back into the api itself you'll see a much more browsable api it's a little bit better formatted and there's a lot of cool things that we'll end up seeing with this and we can also test our apis in there as well so speaking of testing our apis let's go ahead and take a look at our views here what i want to do now is actually change this to being a post request a post request only and what that means then is i'm actually going to be getting request data so you might be tempted to say request.post here and sometimes this is okay but usually it's going to be request.data and that's especially because of how our api endpoint or our actual api client ends up working is we're going to send in json data and we want to change this to simply post now this is just a http method change that's it technically speaking there's really nothing else going on that's that much different than just the method itself that's happening and the post method is typically locked down and there to ingest data and we want to do this in as secure of a way as possible so the rest framework does help this a lot if we were using a pure django view and we used something more like the json response let's actually take a look at what happens here if i do a json response with this we will take a look um so before i go any further now i've got this data here i will just echo that data back essentially so let's go ahead and run our basic client here with that post request going so python and pi client basic.pi we hit enter i get this forbidden csrf cookie not set now that's because api views for the rest framework don't necessarily need that cookie set inside of django that cookie absolutely has to be set it's one of those security features that django has built in which is a good and bad thing in this case we're going to consider it a bad thing because we're not needing to use that we want to just use the api view itself and again we'll go ahead and declare that post request in there and so now if i run this again now it actually echoes back what i have in my post request so what i want to do here is actually just change this to being title and we'll call this hello world so what i'm doing here now is i'm going to send this data end it's just simply hello world right now it's just pinging right back to it right so it's getting that data and sending it right back of course now i want to actually send it through my product serializer i want to validate this data that's actually one of the other pieces that the serializer does really well so we can go ahead and say serializer equals to this and now we're going to go ahead and pass in data equals to data as in the request data to make things even more simple we could look at it like that and again it's not request.post and it's also not omitting data equals which would actually we could take a look at what that ends up happening if we do that then what we can do is say if dot is valid then we can just go ahead and print out the serializer.data and i'll just go ahead and set the data equal to the serializer.data so all this does is it makes sure that the data that's being ingested the data that's being sent to this api endpoint it's just checking that it matches how this data is formatted okay i'm going to go ahead and do one real quick thing here and say try and accept return none for my discount for a moment we'll implement a little bit better of a method shortly but for now i'll go ahead and add this in and we'll go ahead and run this now it's pinging back well it has my hello world but it also has this other data in here as well that data of course is coming directly from these fields right here now some of the fields are missing because there is no data but also because well we aren't actually doing anything with this serialized data in other words i can save this data so if i go to serializer.save this is actually going to be our instance itself this is going to be where we can actually run this data itself so i'll go ahead and say that we'll print out the save data and take a look at what that does and then we'll go ahead and run this and now i get this object of type product is not json serial-able right so in other words now i need to bring back that serializer.data instead of the actual data itself this is actually the instance itself so you could do stuff with that instance from the serializer too this is very similar to if you know django forms really well forms dot save or whatever the form is it's very similar to that the only difference here is the serializer you cannot do commit equals to false it's not necessary you only call save when you actually wanted to save that data and that's it okay so if i run this again now it actually saves this data for me based off of all of the other parameters of the model itself and then it returns the enriched data it has all of that other data for us which is fantastic let's leave the save off for a moment and really just have that serializer data printed again okay and then back into my client here i have that title still i'm going to go back into my models as well or rather the serializer as well and i'll return this item here so we can address what happened here so if i save this and run it now it's going to give me this error and it's saying collections order does not have the attribute get discount okay so what's happening here is we don't actually have an instance anywhere so inside of our view this actually created an instance this is not a created instance there is no instance data that has been enriched in all this in other words nothing was ever saved in the database which means that we have no real way to access this instance method because there is no instance this is the only way to create an instance from the product serializer now if you aren't familiar with how these instances work and stuff like that you'll be like i don't get it maybe maybe not but the idea here is when we use a serializer method field on a model serializer it's assumed that there's an instance attached to it because there's a model attached to it but as we saw you don't necessarily have to have that so what i can do to hedge this is really just say if not has attribute or has a ttr this object or whatever is being passed in here of id then we go ahead and return none now when it comes to an actual instance of a class we could also do if not is instance of well the object the product class itself we can also return none for that both of these things serve a lot the very similar purpose as far as checking this object data this data that's coming through so we don't see that error that we saw whereas like has no attribute get discount now the only way you would know about this is by going line by line and commenting things out and all that i intentionally wanted this thing to break so we could see something like this like when things go wrong how do you bring it back anyway so now that when i run it it still is validating this data but what it's not doing is it's not adding additional data that wouldn't have already been part of this serializer right it's not adding the price that's from the database that's not touching the database there's nothing instance related it is adding the content which is curious i think the only reason for that is because it can be blank and true that is something you'll have to test out my discount makes probably the most sense is because it's actually trying to go off of a product instance and looking for that attribute but in this case it's not based off of the model instance at all it's only based off of the serializer and the serializer method field okay so that is ingesting data and there's there's a lot of things built into this and baked into this that you know might not seem that important at first but the keys are here one we have a serializer it could be fairly broad or kind of small it can only just have fields and stuff like that what that will do is verify the data that's coming through does match the requirements of the serializer first second the requirements of the model in this case the requirements of the model are title that's it the title is the only thing that's required so going back into basic if i change this to just content here or whatever any any field name really and i try to run this again now i get another error right so i can't actually return that error so going back into my view here i'm actually this is no longer valid so one thing i could do is return a different response like something like a message saying invalid data not good data and then a status of like 400 or in this is valid method i can actually raise an exception in here that will also catch what that data is so before we do that raise exception i'm going to take that off and save it run this again now it says invalid not good data right it doesn't give me a lot to go off of there so if i do raise exception what i'll end up seeing is a more robust error message for this request so this is now going hey your data that's coming through here does not have the field title so if i put the field title in here and just say you know none it's save it and hey now it's giving me a little bit better of an error that it can't be null right so now i can of course say abc123 and save it and so forth right next if we did something like price and we did abc123 four and try to save this one it's now gonna say invalid number so this is the validation part of things that not only actually validates it on the back end actually in the view itself but then if we wanted to let them know or let the clients know that you're messing up we can raise this exception if we don't want them to know we can just say hey this isn't good data pretty simple i think overall although there are a number of layers that like might take some time to get your head around that is going to come with testing these things out over and over again now serializers and views are critical to the django rest framework absolutely critical they're like the main thing there are other pieces to it but these things in combination together are what will experiment more a lot more going forward to really really understand how the django rest framework ends up working so even if this was like a little uncomfortable or just not fully sinking in yet hopefully after we do a few more things with views and serializers this part will make a ton of sense all right so now we're going to go ahead and actually jump into generic api views so initially i'm going to introduce a lot of these generic api views and then we'll peel back the onion a bit to better understand how they actually work after you get them actually running so let's go ahead and jump into our products into views here and i'm actually going to get rid of all of the django based stuff and really just use the rest framework so the first thing we'll do is import from the rest framework we're going to go ahead and import our generics here and then we'll also import from.models we're going to use our product model and then from dot serializers we're going to import our product serializer okay so i'm going to create a class a function or class based view rather and we're going to call this our product detail api view okay so i'm using the retrieve api view inside of the django rest framework so this is going to be inside a generics.retrieve api view okay so the things that make this up are plentiful but we're going to go off of just the fundamentals for now the first thing is simply our query set so we can do product.objects.all now of course if you are a little bit more advanced in django you know that you can actually write custom query sets so if you wanted to change that query set you can just do something like git query set we'll go into that a bit more later but for now we'll just leave it as query set next we want to actually serialize this data even if it's one item or many items we just pass in something called a serializer class and then our product serializer now there's something else that's really important to know about when it comes to a detail view of some kind a detail view will get one single item so what also comes in here that i'll just comment out for now is the option to set which field we want to do and perform a lookup on this query set so think of it like product.objects.get what is that lookup field that you have here in my case it's pk and then what's the number or what is it that you're looking up by now of course if you do something like abc that's an invalid lookup and we'll take a look at that as well but anyways that is all we need to do for this view this is pretty simple and we'll see the result in just a moment but the idea here is just how powerful generic views can be and how powerful class-based views can be if you fully understand what's going on i hope at this point you also already have an inkling as to what's going on based on what we did with this api view home right what we practice with on there so now that we've got this let's actually create a url to handle it so inside of products i'm going to make a urls.pi in here this of course is just to keep all of my product urls in one place that's it so this entire app i want all of the urls related to this app in one place and this is where i'm putting it so i'm going to do from django.urls we're going to go and import our path here and i'll do from dot import of views and we'll go ahead and add in url patterns and this of course is an empty list here that we will put our paths in so the very first path i'll do is related to this particular view and so i'll leave the path itself out for a moment and we'll do views dot and this is going to be our product api view as view i'll also come back to this in a moment okay so what is the path that we want to declare here well if we think about our lookup field we want the specific lookup field what data type is the primary key by default now if you don't know what it is then yeah go check it out because each model itself does have a primary key in here by default now that primary key is an integer so we can actually feel good about our url path being what data type it is so int first and then the actual field name next or really the keyword argument we want to use but in our case since we're using the generic api view here we basically want to put whatever our lookup field is as the keyword argument in our url parameter cool now of course if you're newer to django and the django rest framework you might be like everything i just told you might be going over your head and if that's the case if you're like i don't understand anything that you just said about urls keyword arguments and all that check out my try django series pretty much any of them because i do talk about it but certainly try django 3.2 and up we'll go into this specifically for these kinds of paths here now let's talk about this right here this is different than what we had in our api where it's just simply the function name itself so every once in a while you'll see class based views written like this and every once in a while you'll see them printed a little bit more like this so inside of the view file itself you might see something like product detail view and it equaling to whatever that value is and then you bring this in here instead it's kind of up to you on how you want to design that pattern personally i think this is extra code but it is a little bit cleaner and it maybe looks a little bit like a function based view i personally don't do this ever i just do as view but it is nice to keep it in here to know that it is a possibility if you are interested okay so now we've got a view function we have our urls of course we are serializer and our models it's time to actually bring in the urls to our django main configuration urls which in this case i'm putting into urls.pi i'm going to copy this path because it's roughly the same as this path except we'll just put products here with the trailing slash and of course there's going to be products urls now it's important to note that the api urls i actually could have roughly speaking the same path in fact i could actually go in there i'll just put a little comment for it because i'm not actually going to do this but let's go ahead and put a little comment as to what it could look like it'd be something like that and then including the include import up there but you could do something like that where all of your api urls are in one place this is something i end up doing if i have my django project doing a lot of other standard django views in conjunction with a lot of the django rest framework views right so if i have a lot of api stuff and a lot of django views then i put them all pretty much in one place for the api and then another place before the django project itself that's what's the beauty of this includes stuff it's really really powerful on how that all works okay so with that all the way we now actually need to actually test this endpoint here and so what i'm going to do is inside of my pi client i'm going to go ahead and create detail dot pi and it's roughly the same as what we had before right so we've got our endpoint here we've got our our actual request itself and the response itself so i'll go ahead and copy this and i'm going to delete a bunch of stuff just to make it clean and really concise as to what it's trying to do here okay so the first thing is it's not a post request it's a get request we are looking up data now the data we're looking up is related to products and then some primary key in my case i should only have one product okay so this is going to be my endpoint here and that should correspond to this url except i don't actually have a trailing slash so in this case we want to be explicit about trailing slashes in this case i added one if you don't have one then your lookups might not work correctly okay so now that we've got that let's go ahead and go into the root of our entire project and run that detail.pi so python pi client detail dot pi what do you expect to see i hit enter what do you know it actually gives me the detail of that data now of course another thing i could do about this is i could actually look at the django rest framework browsable api that's activated by default and i can see this exam same stuff and if i go into a product that maybe does not exist like 2000 it just gives me a 404 not found which is fantastic this is built into those generic views it's built into the django rest framework i didn't have to touch any of that logic whatsoever so of course this is only the first of several of the gene generics we want to look at the actual generic views we want to look at before we start unpacking it before we really nail down all of the nitty-gritty of this all right so now we're going to go ahead and take a look at the django rest framework create view and so to do this i'm going to go ahead and say class the product create api view and of course it's create generics.create view now and the things that we need to include are the query set so product.objects.all and then also the serializer class and our product serializer hey these look awfully similar in fact they're the same thing which is pretty cool now i'm gonna go ahead and do product create view and of course we will also have it in as view i will stick to this naming convention although like i said before i would always pretty much put that in to my urls okay so with this in mind let's go ahead and bring this in we'll go ahead and declare a new path here now you might be tempted to add a slash there i'll tell you why you shouldn't do that and we'll go ahead and use our product create view of course now the slash here is basically doing this we've got slash api products this is where these urls are mapped to from urls here right so api products notice there's a trailing slash there and so if we go into our urls here we've got this path this will give us two slashes so we can leave it empty i think django will also warn you about that but i just wanted to make sure that you knew about it for sure okay so now that we've got that let's go into our pi client here i'm going to create a new one and this is just simply going to be called create so inside of my detail view or the detail.pi client rather i'm going to go ahead and use the same thing roughly speaking but this time i'll go ahead and just do our new create endpoint now it should be mentioned that you know you might be like well why don't i have it at slash create right and this has to do with what we'll do in the next portion okay so for now we'll leave it as this so it's just there and i'm gonna use this post method again as we've seen and we'll just save it just like that that is i don't have any other data that i'm passing so i just want to run python pi client slash create dot pi hit enter gives me title this field is required great the basic validation is still working so now i'll go ahead and put my data in here and we'll go ahead and add that title in and say this field is done or whatever you want to call it and we'll go ahead and run that create and i'm still getting that title oh yeah i still need to pass it here so let's go and do json equals to that data and we'll go ahead and run it again and now i'm actually getting it with that title and of course i can continue to make this right i could do this over and over and over again and it's actually creating new instances which is actually kind of hard to tell based off of what's coming through here but it is definitely there and if i put in like let's say for instance price and we do 32.99 or something along those lines and run that again it actually does the correct price so that would be a way to test it now one of the things we definitely want to work towards is with this create view perhaps if i'm an admin user maybe i want to show a different serializer class that is something we will work towards that's a little bit more advanced than where we're at right now but one of the things that you definitely might want to do is in this create method like when it's being created perhaps i want to assign something to that data so let's go ahead and try that out so perform create is a method that you can do on a create api view here now you could write this method on any other actual view it just won't be called unless it's a create view so on perform create it takes in two things that is the instance itself and then the serializer and then in here i can do serializer.save and if i had my user instance which i do not at this point i can actually assign the user just like that so we'll probably come back to that and i can also print out what's in the serializer itself and then we just want to call the serializer and dot save and maybe we should spell it correctly okay cool so let's go ahead and save that and let's see what that print statement is so now it should actually print out what the serializer is so it's going to run it and now we get that serializer data right so we've got a number of things that are coming in here that are related to the serializer what if i do serializer.cleaned or rather validated data let's try that one and run it again now i can actually see the data that is coming through or that's actually in there so what i can do is say title equals to serializer.validateddata.get title and i can do that same thing for the next part which was what do we have content okay so content okay or none if content is none then we'll go ahead and say content equals to the title and that way we can actually see this in action so content equals to content and let's try that again with a slight change now i get the content that's coming through based off of that title uh just a really simple way to add additional context to this serializer in this create method now what do i tend to use it most for and that's this right here or you can also send a signal here if you're familiar with django signals you could send a django signal here if it's not necessarily related directly to the model itself because there's a whole discussion we can have on django signals but anyway so that's the baseline of the product create api view really really nice so now what we're going to do is we're going to take a look at the product list view now we're going to go ahead and do our list api view it's really simple based on everything we've done so far so we'll go ahead and do product list api view shocking right it's exactly that okay so that is the basic of the product list api view and by all means wrap that into the urls you want to use now i'm actually not going to use this method why that's because of our product create view i can actually do a product list create view and really just changed my api method to adjust for that and again it's still basically the same except it's just slightly different and that is actually why on my route here i have it like that so then what i could do is i actually don't need to change create dot pi at all but what i can do in my pi client is list dot pi and using the same endpoint i can list everything out but just change the requested method how cool is that so now we have a way to list these things out as well as how to create them so pi thon slash pi client and list.pi hit enter and there we go so now i'm actually listing out everything in my database and of course one of the nice things about having the rest framework by default we can actually look at the browseable api here and this is giving us all of the listed view and hey what do you know i can even try out submitting this data in as well which is pretty nice right so i really really like the fact that i can just do those things really fast and really really succinctly now the thing is what's the next part of this like how can i combine other kinds of views now there are other kinds of views that i actually left out and that is updating something or destroying or deleting something and so that's what we'll do in the next one is actually look at the view that does both of those things so combining views like this is really really common unless of course you need to have different endpoints now the other thing is as i mentioned before you can have different kinds of values for different users including what they can and can't do like permissions that any particular user has that stuff will come after we do some more authentication and understand how the views work a little bit more because we'll take a look at that too each one of these functions has a very specific purpose to it that is often based off of the actual http request itself but let's create view is a good example of this if it's a post method it will assume that you're trying to create something if it's the get method it's going to assume that you're trying to list something or list a query set we're going to look at this same idea all three of these things that's our list create view and our detail view as one single function one single function based view so what i want to do here is i want to think through the logic first we're going to define the product alt view and it's going to take in a request and then args and keyword args so the idea here is we're going to be distinguishing things based off of the request method so if the method is get then we will do either a get request like actually get data so get request as in a detail view or we will do a list view this of course is going to be based off of the actual url so that means i need to get my url args we'll talk about that in a second as well so go ahead and say pass for a moment then if the method is host then we'll go ahead and create a an item right and so we've actually definitely already seen a lot of these things just in separate parts so if we look at the create method for example in views.pi we currently have that create method there right so i could quite literally copy this and bring it into if post right and so i've got my request here there's our post method and i need to bring in and change this to an api view itself with a few imports so i need to allow the get and post methods in here so let's go ahead and do those imports now so from rest framework dot decorators we're going to import the api view and then from breast framework and dot response we're going to import the response another thing i want to import is from django.shortcuts we're going to import git object or 404. now another thing you can absolutely use is from django.http we can also use http 404. i'll show you just briefly how to do both of those things okay so first off the listview is probably the easier one so what i'm going to do here is create the listview first off the query set is product.objects.all right or whatever query set we end up using so i'll keep it in as just simply query set i'll write it out as query set but a lot of times the variable is just qs that was what you'll see often next to actually serialize a query set it's also pretty easy first off we'll do our data and we'll use our product serializer again this time i'll pass in that query set as an argument as well as many being true and then just grabbing the data like that and then we can just return that response of that data okay so far so good so how do we actually do a detail view now though that's the big question here so the list view is fine but how do we get that detail view well this is going to have everything to do with the keyword arguments that are passed here so the keyword argument is going to be primary key or at least that's where we're going because we're going off of the default of the detail view here that lookup key and so basically what i want to say here is if pk is not none then i'm going to assume this is a detail view otherwise we will assume it's a list view and that pretty much covers the gambit and realistically i don't need this else clause because i will return a response in here as well so i probably could just go back just like that the else clause is just to make it a little bit more clear i guess but i think this is pretty clear so what is it that we're going to do with this primary key here well first off i need to get the object itself right it's not a query set anymore so i do want to show you how to do it with the query set first though so query set and then we can just go ahead and do filter this down and say pk is equal to pk and really i could say if query set uh you know if it does not exist so you could do that it does exist or you could say it does not exist this is where i could do that raise http 404. that would actually be handled correctly within the django rest framework because of this decorator which is so cool it makes things really easy on us but that also means that i can do something more like this where it's just grabbing the object or obj and that's git object or 404 the product class or the product model itself and then the lookup field name and then the actual lookup value the keyword argument here okay and then once we have that assuming that it does give us an object or it does not raise an http 404 which it could if that object does not exist which we will certainly test in a moment then i'm going to go ahead and say my data is equal to that product serializer again passing in that object this time many of course is going to be false we don't need to declare that that is the default but i'll go ahead and add it in for now and there we go so this product alt view is now a function based view that does the same as pretty much everything else the one exception that i did not update was this right here so this perform create method the exact same data is going to come right here so i can just paste this in here and let's go ahead and tab it back make sure it's tabbed correctly and i'll tab this over and i think that should solve how that's all done including saving the instance itself and so that is our actual data oops i tab that in a little too far but there we go so now it's identical and it's all in one single view so we could of course test this and we should in our urls i'm going to change both of these to that one single view and now with my client i'll go ahead and do the python and pi client and then we'll go ahead and do detail.pi i should get that data then we can do the list.pi i should still get that data and then we should also be able to do create.pi and again still getting that data and of course the last thing is maybe we want a not found item or a not found actual view or rather look up view here with a id and whatnot so inside of my client my pi client here i'll do not found up high and it'll just basically be the detail view but some ridiculously large number here that i certainly do not have in my database although that is an integer hopefully this will work as intended we'll go ahead and do we'll take a look at it so save it as python and pi client slash not dash or that should be underscore no dashes and python modules what was i thinking let me just rename this real quick underscore and there we go so python pi client not found we hit enter this integer is too large yeah i thought that might be too big okay let's try that again and now we get a not found very cool okay so let's go ahead and well call that a day so the big thing here though of course yes i i definitely did this really fast and i personally already know how to do all these things but the problem with this view is not that it works certainly works but it's confusing right so if you are familiar with the django rest framework and you just look at these generic views not confusing in fact we don't need this list view at all i can comment that one out this is these two are not confusing at all i think they're very straightforward very easy to do very easy to run with this on the other hand i have to be like wait wait what's going on here hold on okay on the get method blah blah blah right it just adds confusion this is why using those generic views is another like great thing is because well we can all reuse them then i can look at your code with a generic view and have a sense of what's going on you can look at my code and have a sense what's going on if you see these function based views which tends to happen in function based views a lot of the logic is like all over the place again because function bay views are so flexible because they have to be you have to write them from scratch class based views are also flexible they can be but it's a lot of modifying to make that work function-based views aren't quite like that but nevertheless it's still cool to see how this is all done and i think there's actually a lot in here that can help illuminate a little bit more about the django rest framework especially like this query set thing so if we needed to enrich a serializer this might be one way that you end up doing that with a serializer method field which is something you can check out later but for now i'm going to leave it like this as far as the views are concerned but of course in my urls i want to bring this back to the actual ones that i was using so the product detail view and also the product list create view just like that oops didn't mean to do that let me just open that back up okay great and so of course now the next step would just be to do that edit and destroy views not necessarily together but the idea here is we have a better understanding hopefully to some degree why the generic views are so good and you know why the function based views are still good they're really flexible but they're leads to a lot of confusion or potentially a lot of confusion in there now once you get experience like me maybe you're going to mix and match how these things work but realistically this alt view is not something you'll use very much it's really meant to illustrate what you could do on one view and what you could also do thinking a little bit further is you could also change to http methods to update and then maybe destroy to you know delete which is definitely something that you could do all in one view in other words i could do all of crud in one single function based view or a class based view as well that's just not something i recommend doing because of this hazy logic in here as i mentioned let's keep going now we're going to go ahead and add in the update api view as well as the destroy api view now both of these views are very similar to the product detail view except they might contain additional data and they use a different well what do you think http method so let's take a look first off i'm going to go into my urls actually i'm going to design two new urls in here to handle the different methods i want to handle so update and delete okay or destroy you can think of it as delete or destroy but i'm going to leave it in as delete because that's the actual http method that we'll use so inside of our views here we're now going to go ahead and copy our detail view and i'll paste it below and this is now just going to be called our update view right and change this as well and then finally here okay so now that we've got this i'm going to go ahead and declare that into my update notice that it is almost identical to the detail view which means that i also have this lookup field right here in this case i'll just declare to make sure that we know for sure that our update view does have this so the next part of this is defining a method called perform update this is going to take in the self and serializer and the instance is going to be equal to the serializer.save okay so this instance is well what do you know it's identical to the instance that comes through with perform create so i can do additional things to this instance if i need to one of those things could be related to the content as well like if the instance.content well let's let's try it out so if not instance.content then instance.content equals to instance.title and then of course do we need to save it that is going to be the big question and the answer hopefully is clear because it says well it saved it right here so let's go ahead and not save it initially okay so we're going to save the file views.pi and we'll go ahead and see if our product update view ends up working okay so the first thing i want to do is go into my detail view i'm going to copy this one before i copy it i'm going to run it so python and pi client slash detail dot pi i now see hello world is in there so i want to change the title let's change it to something different than hello world so inside of pi client i'll just do update.pi again it needs to be a object that actually exists otherwise it's not going to work the data that we want to pass in is the field that we want to change so in this case i'll change the title to hello world my old friend right and maybe we want to change the price from being zero dollars we'll change it to 1299 something ridiculous cool then we'll go ahead and use the put data here and we'll pass in our json just like that simple enough right so now i'll just run python client instead of detail we'll use update.pi hit enter i get the put method is not allowed of course i have my update view here let's make sure all of my urls are saved i should have my update view there everything's saved up let's run it again still getting the put method is not allowed that's because my endpoint here should be update cool just a slight error in where that is now hello worldwide friend works if i go to the detail again i should have that new updated method in here and of course if i wanted to change it back i totally could now i don't have like revision control like i don't see what the old version is but i can change it back to it just like that really simply in the case of updating now deleting is not a whole lot different this time what i'm going to do is i'm going to list everything out and i'm just going to delete one of my last ones so one of the last ones in here but unfortunately i don't have my id's listed so what i'll do then is in my detail i'll go ahead and say 10. let's just see if 10's in there get a detail 10's in there great so go ahead and copy this whole thing and naturally this is only going to work one time so let's go ahead and say delete.pi we'll paste in here in fact i will go ahead and say git id or let's go ahead and say the product id equals to our input of what is the product id you want to use and we'll put a new line in there and we'll go ahead and say try int let's say product id is equal to the integer of that except and print the product id is not valid i could probably make this a lot better okay um and then we'll just say if product id and pass that in here just like that there now it's a little bit better of a test or a delete test that is and we'll go ahead and say delete and now this is going to be the delete method directly there just to the endpoint that's it okay so now let's go ahead and run python client and delete dot pi what product id do you want to use i'm going to say 10 delete method's not allowed hey did we use the wrong endpoint again nope we've got the right endpoint let's go ahead and make sure our delete view is there it is not so product delete view and in our views itself did we even do it we did not oh no okay so let's go ahead and copy this this time of course i can do perform destroy this is not going to be the serializer this time it's going to be the instance itself and what i really want to do is if there's anything i need to do with the instance i can do it here otherwise i'm going to run super.perform and destroy here as well of that instance and we need to make sure that we're using the destroy api view all across the board and product destroy or delete view there we go and so once this is run it will delete it and it'll be gone forever so product destroy view let's try that again what id do i want to use 10 now i get a json error because it's actually not going to return json instead the status code will be 204 so in the delete we want this to be status code and well we could assert that it's 204 or i could just say it's equal to 204 so we'd see that value now with product id i'll just go ahead and say 9 because i know 10 existed and what do you know it says true now what if i do one that probably does not exist i'll get a 404 and false great so now we have a update view and a destroy view which makes a whole lot of sense so there are well one of the biggest glaring problems here with all of this is sure it works we now have done crud that is you know create retrieve in this case delete or detail rather update and destroy or delete we've done all of crud right but the thing that we haven't done is lock these things down we don't have anything related to permissions we're just letting anybody do anything deleting anything and everything so that's certainly something we'll still need to do but before we can ever even do anything related to permissions we're going to need to do authentication which is pretty interesting now before i go much further with this though that permissions and authentication stuff is what we will cover going forward but before we do that i actually really want to see these api views broken apart into the core class based view so we just have a better sense of how this is actually functioning and how we might accomplish something similar to the function based view version but rather with an actual class based view version that's what we'll do in the next part all right so now what i'm going to do is something very similar to this product alt view but using a class based view in the django rest framework so to do this we are going to declare class and i'm going to call this my product mix-in view and it's going to be generics dot generic api view and we're going to define our first method of get this is going to be self request rs and keyword rs and it's going to return something so the biggest difference between class based views and function based views is we don't actually write conditions for the methods we actually write a function for the request method right so in the case of this product alt view we have a condition if it's a get method we have a condition if it's the post method in a class based view we just write an actual method on the class itself for the different request method the different http method of you know post or get right so initially i'll start with just the get method itself and we'll come back to the post method okay so what is it that i'm trying to do here now initially speaking there's a lot of different things i can do like the list view for example i could do start with the product list create view but let's make it really simple and just do the list view to start out very similar to like what we did here but instead of having to write a bunch of things we're going to only write a couple so the first thing that i want to do is i want to actually build it very similar to how the generic list api view works and that is by using mix-ins so we're going to go ahead and import the mix-ins here at the very top from the rest framework and we're going to import the mix in right above the generic or really before the generic api view we're going to do mix in and list model mix in okay so what this does is it provides us with the ability to declare our query set like product objects.all and our serializer just like we saw before and that of course is going to be our product serializer and now in this get here i can just call self.list request args and keyword args so this list method here that right there is coming directly from this now in this case i'm actually handling this list method from a http get method but if for some reason i wanted to change it to a post method i totally could do that i could do that just like that now it is different than the product list view by default or the generic list api view by default it now goes off of the post method now this same concept actually can be applied in other places right so coming back into this product list view here i could actually implement that same idea here i can use that same post method because this mix in is in here by default i'll show you the documentation for that in just a moment but the idea here is when we want to use mix-ins that will give us the ability to have access to some of the methods that are built into the mixing this is a little bit more clear as you use it more but also look at the documentation so in the documentation we've got our generic api view here and we've got a bunch of basic settings such as query set serializer class hey what do you know even a lookup field in here great now when our when we look at the list model mix-in we see that it provides a dot list method that takes in a request that's it it doesn't say how to use that list method this is how you use it you put it on some sort of http method now i'm going to keep it in with the standard method here the reason being is now i'm going to go ahead and test this out with our product mix-in view and of course it's going to go based off of this product mix in as view and we're going to go ahead and run the client here so python pi client and list dot pi so i hit enter this should list everything out as it did before now of course in my urls i just want to change this to my product mixin view now again still listing things out if i attempt to do that i'm getting a invalid error here so i put serializer this should be actually serializer class which is what we've been doing up until this point and it will also tell you this is the error here you need a serializer class or implement the get serializer class method which we could talk about later but for now i'm going to go ahead and run this again and again it lists things out but there's of course one big caveat and that is if i do create and hit enter it now says the post method is not allowed interesting what if i actually change this to post like i did and now run create now it doesn't care what the data is it's just going to return back that list method here because that's how i implemented it if i go back to that list method where it's doing a actual get it's now saying the get method is not allowed right so this is actually what's really cool about using class based views in general in django and it's also what's cool about using it inside of the django rest framework if you need to augment or change things however you see fit now let's actually implement one more step in this product mix-in much like we did with our old view where we had that primary key in here so very similar to this but not quite the same so again i'll go ahead and declare my lookup field the default being primary key now this only matters for things that it matters to in other words the list model mix in does not care about this argument at all so you don't need to set it and also the retrieve model makes in so mixins.retrieve model makes in which of course is for our detail view this is the only one that does care about this lookup field so it's going to default to being primary key but if you need to change it this is where you do that so now what i want to do is i want to print out the keyword args or let's print out the args and keyword arcs before i attempt to use the retrieve model mix in let's go ahead and see what the args and keyword args on that get method by changing of course our detail view here to our mix-in view so yet again i'll go ahead and go up and to our detail pi client detail hit enter and let's spell detail correctly hit enter and i'm getting a list view still but when i print it out notice that i've got my args and keyword args being printed out on django so in here i've got my rx and keyword arcs now i actually want to keep my primary key in my keyword args so all i'll do here is do pk equals to keyword rx.get pk in other words i don't need to set pk being none here that's not necessary and well frankly it wasn't necessary down here either you could do that same method up here as well so now we would just say if pk is not none then we will return self.retrieve this time and this is going to take in the request the args and keyword args and those keyword arcs since i just using the get method those will also be passed in here now and so it's just going to change what that git method responds with based off of the mix in so if i run it again i get a detail not found so let's go into detail and i see that it's at 10 i remember deleting that one so let's change it back to one that one i did not delete so now it is showing me that data and of course it's still based off of this same random product mix in right something i won't use in the long run because it's a bit convoluted but it's really useful to understand things that are going on here and also see how convenient these model mix-ins are and can be because realistically at this point i now have a view that handles well two things in my urls the list view and the detail view it did not do the create view so let's go ahead and add in the create view into this whole mix so make sense dot create model makes in what do we need to handle here well the create view is typically a post method so we do self request args and keyword arcs and of course it needs to return something what does it need to return well if we go into the documentation and go into the create model mix in we see that it provides a create method hey what do you know i can just return back that create method you call self dot and that create method now again the reason that we have that create method is because i have this mixed in in here if i did not have that mix it in here this would not have a create method by all means test that out on your own so let's go ahead and run create now and i actually don't have to change anything because of the fact my urls i had that list create view in there anyway so i run this again and we hit enter and sure enough it ends up working so the challenge i want to leave you with is how do i actually augment this to handle my other views like perform destroy and product update i'll give you a hint it's actually incredibly similar now before i actually go any further i do want to say hey wait a minute i did this create view but what i didn't do was this the or that's perform update but the perform create can i just copy this and bring it down here this time just change my content slightly so just say if it's none then i'll just say this is a single view doing cool stuff save it now let's go ahead and try this again notice that this one says none now i'm going to run it again and what do you know this is a single view doing cool stuff is in there so yes those methods are still available and the only reason that let's say the create api view has that method is because look at what it's extending the create api view or the generic api view along with the create model mix in quite literally what we just did it's it's unpacking this into one single view in other words if we looked at it it's just this so the create api view is the model makes in and the generics here there we go that is our create model create api view and you know you can actually look into this yourself too by going into let's say the crit list create api view not running the code rather coming back in here let's close that let's actually go to the definition of the code this actually takes you inside of the virtual environment package itself right takes you right in there and what do you know we've got our mix ends and all the methods exactly the same this is what i just broke down for you and of course you can also look into those even more deeply as well like the actual model mix in and go to that definition and now you see a number of things that hopefully you're starting to get a little bit more familiar with as it relates to all of this stuff right maybe this gets serialized or things a little new to you but we've seen is valid and raise exception self.perform create well we've implemented that and what do you know it actually passes all those things in as well as headers and some of the response data right so we've already seen a lot of this stuff and so this actual mix-in this this product mix-in view which is just a lot of those mix-ins all in one does give us some flexibility here and it also makes things a little bit more convoluted now i don't think you should go this route i think the purpose of this or my intention was that you learn a little bit more about the mix-ins themselves because yes you can absolutely create your own mix-ins just like you can in just pure old django in the first place but i want to show you generally speaking what you can do as well as this model method here um in general to see also you know doing it in function based view or class based view if you have questions let me know otherwise let's take a look at permissions and authentication now we're going to go and talk about session authentication and permissions all this has to do with logging our user in and making sure they can do the things that we want them to be able to do i'm going to start with permissions just so we get a feel for what this might look like so we've got our product mixed in on this api endpoint here that is something i want to change so jump it into urls i'm just going to change it back to what it was originally so product list create view and you should as well and then also the product detail view now when it comes to generic views so any view that's inside of generics we can do this same thing that i'm about to do so what i want to start off with is permission classes and what i want to do here is i want to use if the user is authenticated let's let them do something so i'll do permissions here and just do permissions dot is authenticated okay so we'll save that and we will go back to this endpoint and refresh in here i get authentication credentials were not provided now if i do this same thing inside of my pi client let's say for instance pi client and list dot pi i get authentication credentials were not provided if i do create same thing cool so this is showing me that this user this request does not actually have a user associated to it that has permission in this case the user is just simply not authenticated we will show you how to solve this in just a moment but the cool thing is there's a number of different authentication or permission classes that you can use both things right so permission classes we'll take a look at the default ones we're going to create a custom permission in the next part but for now notice that these built-in ones here we've got allow any is authenticated is admin user is authenticated or read-only and then a few others as well like django object permissions which is pretty cool but anyway so we've got is authenticated or read-only what does that look like well let's go ahead and add or read only to this okay so now i save it and i go to try to create it again it gives me authentication credentials were not provided if i go to listed this time this time it actually gives me back this data so what's happening here with this particular permission is i can actually not send post data i can't use the post method but i can use the get method i can actually return back the list view related things now of course if this was a create api view and i tried to do it again the get method's not allowed because of the type of view it is that has nothing to do with the permission right so if we come back and run that again and now do let's say a create view we've got a 403 error versus a 405 error so just slightly different status codes here that are coming through with the different permissions and the different view types and the different allowed methods now i only mention that is because it does actually have some distinctions that if you are creating an api client you can see what all of those distinctions could end up being but anyway so now we've got this permission class of is authenticated or read only but how do we solve the is authenticated part well that comes in then with authentication classes and that is also a list that we can add in and so i'm going to go ahead and import the authentication module and what we want to do here is we want to say authentication dot session authentication okay so these two roughly speaking are already in the api generics view like you might already have these somewhere listed out and yes you can absolutely use these in function based views as well um they are certain decorators for it so these are throttling notice that there's authentication classes and permission classes that you can use on function based views as well that's not something i'm going to cover right now but there are those decorators if you need them elsewhere but generic api views this is one of those things that's really nice about them so not only can we use it on of course our list create api view as we just have but you can also use it on you know just a generic api view like we did before so by all means test that out but now that we've got this session authentication what i want to do is i want to create a admin user so i'm going to cd into my back end here and i'll just run python manage.py create super user and i use cfe you know whatever password because we're testing things it doesn't really matter there and then i'll log in to my admin account so cfe and then that whatever password now i actually have a session in django so if i go back into this api and refresh in here notice that my user is actually showing up and if i scroll down i actually have the ability to create new things now if i go back into my pi clients though so python and pi client slash create.html or create.pi and hit enter i still get this authentication credentials were not provided now hopefully there's some intuition here that makes sense my pi client itself never actually logged in so if i look at this this is the only thing that's happening for that create.pi now there's certainly a way to have them login to the admin like as in going to admin and logging in and having a session and then using that session to then send data right or post data that is certainly possible and it would be done through something like selenium which would allow to keep that session but it emulates a browser now this is not ideal for the vast majority of apis when you want to expose them to third-party users right so this api right here is really meant for your standard django project so if you have got your django project here on localhost and you actually serve let's say a react file or any other javascript now that javascript can interact with this api and use the session cookies to actually interact with that data and actually use this api so that is something that's fairly important for the session authentication but at this point what i want to do is actually take a look at how to create custom user permissions then we'll go ahead and take a look at token authentication so that our python client here will actually be able to work with the api as well so let's take a look before we create a custom permission i want to talk about the django model permissions this one is incredibly useful for all sorts of reasons as we'll see so the first thing i need to do is i need to log into my admin and create a user i just created a username staff with staff status that's literally the only thing that's new about this right next i need to add in my product model to the admin itself so inside of products in admin pi we'll go ahead and do from models we're going to import our product and just do admin dot site register and product okay so we save that and now my admin user themselves has access to review these products and of course has full access i can add things i can delete things and so on so what i want to do now is i want to take the same url open it up in a new incognito window and run it now the reason i do this is so that i can actually have two sessions logged in at the same time you don't have to do that but it is a nice way to just jump back and forth between these two users so now i've got staff i'm going to log into the staff user and of course i've got for bid in here okay so if i go into just simply admin i don't have permission to see anything or view anything so this is where the django permissions come in right so if i come into my user here go back into staff and i look for let's say for instance products and let's say view products right so can view products bring it over save and continue refreshing here what do you know i can actually now see all of these products but unlike the super user the super user can actually edit them right so when they're looking here this is a read-only view okay so i want to actually emulate this in my api itself but before i go down that road i also want to go ahead and say groups let's go ahead and say staff editor right it's just a staff editor group and so we're going to go back into products maybe it's staff product editor let's change it just to be a little bit more specific and now we can say can change these products and can add these products so these two we're going to select bring them on over hit save and continue so back into my users into that staff user i'm going to go ahead and add them in as a staff product editor now so they can view it and they can edit it okay there we go so we save it now and if i refresh in here now now of course i have those permissions so that permissions on two levels one directly on the actual staff user themselves and then the other one being on the group that they are a part of right two different ones now this group i should probably also have can view product and so now the actual user itself doesn't have distinct actual permissions that are not in the group itself but we're actually going to toggle these things a little bit so we can see how these permissions end up shaking out in the api itself okay so with that in mind let's go ahead and copy this this endpoint here and we'll bring it into our incognito window this of course is to make sure our staff user is the current session we're using so we can see well the permissions we want to use here and that of course is going to be our django model permissions so back into our views for this product list create view i'm going to change this is authenticated or read only to django model permissions okay so at this point nothing should really change i should be able to refresh and see that i have permission to view it and likely add things let's go ahead and change that so i'm going to go back into my staff user on my super user account right so i got my super user account here i'm gonna go ahead and get rid of the staff product editor group hit save and continue and we'll go back into our admin or the api itself for the staff user notice that i can't edit anything in here anymore and this is also true in the admin as well right so we can see what their permissions are right here so the nice thing about this then is of course you don't want to have your staff members passwords right you want to allow them to be able to change their own passwords so they can log into things securely i don't think it's a good idea to have your staff password of course you can always change it yourself right you can come in here and change it to something else if they're doing something you don't want them to do but the general idea is we want to lock this down as much as possible and that's what we just did so the other thing is if i go into a product let's say product one i still can't edit it i can't do anything i can view it let's go actually one update wait a minute so i've got the update method here can i actually update this right now it's a method is not allowed on this update api so let's go ahead and try and change the title so new title for this one exclamation mark hit put what do you know it actually does edit this how's that possible well it goes down to the view right i didn't actually change the permission at all so i can update and change the permission and i probably should but something else you should notice is perhaps the session wouldn't necessarily show up there in this case it did but that's where our permissions we have a glaring weakness in our current setup the permissions are only on one single view right so two things are at play here we've got a new kind of permission that's based off of the user's permissions which we'll talk about more in the custom one as well so we can be a little bit more distinct to that but it's also based off of the fact that i have the permissions declared on any given view so the next one we're going to go ahead and create custom permissions and make sure that our default permissions and authentication are everywhere then we'll actually start to look at the actual token authentication i promise but before i actually go there the other thing i want to take a look at is this model permission one last thing about it i want to go into products okay so in the products i can see everything so what i want to look at is in my list or actually for the staff user itself i want to get rid of this can view product here and we'll just hit save and continue so at this point this staff user has no permissions to do anything we can verify this by going into the admin they have no permissions to view anything if we go back into the list create api view though what do you know it's actually listing out all of these products now this is not what i intended i intended for them to have none of those actual permissions this is why we actually need a custom permission is to change this ever so slightly at least for this particular model itself okay so if we look at the model the django model permissions the main ones are related to making changes these are the non-quote unquote safe methods we'll talk about that a little bit too but basically if it's an http post put or patch or delete that's where the permissions really come in this is the default behavior if you want to include a view model permission that's when we actually create a custom model so there's many reasons to create custom models but this is one of them and so in the next one that's what we'll actually do is we'll actually create a custom permission to handle this and then we'll apply those permissions as well as the authentication requirements across our entire api let's take a look [Music] so we left it off with some permission issues that we want to solve in this one with a customized permission so if we look at the django admin for example this staff user has no permission to view or edit anything but of course if they go into the django rest framework api view they can actually see everything they might not be able to edit anything on this particular view but they can definitely see everything so that's what we want to change here and to do this we're going to go ahead and be using the custom permissions here so if you go into the documentation and go to custom permissions you'll see all sorts of really good information on how custom permissions work and also two examples of permissions that you might want to actually include in your project so this block list permissions a good one because you can actually track ip addresses and block them if you need to that's a way to do it the next one is owner read only can actually check whether or not the owner is the one accessing this that is the foreign key user i'll show you where that would end up being in any given object to actually make an edit otherwise it's just going to allow you to view it again read only and so if we look at the model itself what you would essentially see is something like owner equals to models.foreign key of a user and then now this actual model itself an object of that model can now run this particular condition and be true or false depending on who's trying to access it and their authentication cool so now what we want to do is go back to our django model permissions here like we've been using and we want to well actually override this model permissions and it says set the params map property now before we set that map property here what i want to do is just start the process of having our own custom permission so it's this right here we're going to go ahead and come in to our products app and we'll go ahead and create permissions dot pi we'll do from rest framework we're going to go ahead and import permissions and then we'll create class is staff editor permission and there's going to be permissions dot django model permissions and not too many s's and there we go okay so default values here we can say define has permission and it's going to be self request view and we can return true or false right and we also saw the other one which was has object permission and that gives us also the obj or the object itself and this is where i could do something like obj.owner equals equals to request.user or of course i could also do that method where i'll just show you the example directly you could check the request method if it's in the safe methods like if it's get header options like it says right there but of course i'm not actually worried about the object permission at this point especially because you know the django model permissions exist and also this example is here so if you wanted to use this example somewhere else by all means bring it in and save it but for now we'll stick with this is staff editor permission now one of the first things that we want to check on this is to check if the request die user dot is staff if they are we're going to go ahead and return true now when it comes to permissions i think it's better to offer the least amount of permissions than the most amount in other words default to they do not have permission and then grant permission when they meet certain criteria and so that's how you do it here okay so what we can also do is we can actually see our user let's go ahead and actually say user equals to request our user here and that way i can just reference that a little bit easier so i'm gonna go ahead and print out user.getall permissions okay i just want to see what this user's permissions are so now that we have some baseline of our actual permission class here i'm going to bring it into my views so from dot permissions we're going to import our is staff editor permission i'll get rid of the other default that's in here and then we'll come back okay cool so make sure everything's running and let's go ahead and take a look at this from our staff users perspective we'll do a quick refresh and what we see in here assuming that everything is saved up we refresh in here and this print statement is not going through because it returns true too soon so let's save that again and we can refresh now and we should be able to see the permissions that this user has there are a none right they literally have a no permissions so what we can do is if the user is staff again doing the least amount of permissions i'm going to go ahead and say false now what i'm going to do is basically say if user dot has perm or has the permission in this case it's the products app dot and the action we want to allow for in this case it's going to be view product okay so view the actual model name and lowercase so the app name all lowercase and the model name and lowercase as well if they have the ability to view it then i'm going to go ahead and return true okay so right now if i refresh in here now it doesn't right i have no ability to see this at all so this is of course to view it what about to change it right to make it edit to it i can do change as well refresh still same thing okay and then we'll go ahead and also add in add and then let's do the last thing which is going to be delete but i'm actually going to rearrange these a little bit to be in alphabetical order and there we go and so obviously i have no permissions whatsoever yet but let's look at the format here it's going to be the app name and then dot the action so let's just say verb underscore the model name okay so that's how it ends up working add delete change view that's it really that simple so if i had another app called you know posts we would do post dot view and then post maybe the model is called post right now this of course is a lot of manual writing but we can still try it out let's save it the main one that's going to work on this actual endpoint is most likely going to be well just view it list because we're doing a git option here so let's go back into the admin with our super user let's go ahead and add back some of the permissions or all of them so let's go into products and view save it and we'll refresh in here and here actually with our staff user and when you know i can actually now view these things in this case it actually gave me the also edit portion interesting so it's actually not doing the permissions correctly right it is sort of doing permissions correctly but it's also really not and that's because the view itself has different kinds of views that are going through in here so different kinds of permissions for each inherent view and what's happening with my custom permission here is if this user has any of these permissions it's going to be true this of course is flawed and not at all what we want we want specific actions that are allowed for this particular view and so the nice thing is the django model permissions has the ability to you know augment and to change but it is important that you understand at least a little bit that there is a way to go through here on these different permissions so the other question here is well how do i actually augment this to work the way we want it to as in how do i make sure that it's view is also one of these permissions well we actually do not need this has permission overall except for the fact that if the user is not staff so i'm going to go ahead and comment this out just so you have a working example in the commented code and then i'm going to go ahead or somewhat working not quite working correctly but it still works as a example then i'm going to go ahead and actually return back super has permission request and view so this is the default permission then and now what i want to do is make sure that they're a staff user so if not request by user that is staff then i'm going to return false so no matter what this has to be a staff user so right off the bat it's already checking that now of course the permissions we actually already have that is admin user it's the same thing right right there that's it so that's kind of cool that that exists so we actually could use these side by side which we'll take a look at in just a moment now for the django model permissions itself how we actually make this work correctly well we can actually go into the definition of this code this is the actual code itself notice that it's inheriting from the base permission which we totally could have done and here's all the code that goes through with it so has permission query set this is a method that it creates specifically for this permission get required permissions as we see here so we've got a number of things that are really cool but the main thing here as it says in this note is just to augment this and so to do that we just bring it into our permission class here we paste this in here and now i've got these parameters and so what i really need to do is just change this git parameter and change it to match that format that we already discussed i'll show you where to find this format as well if you forget so that's going to be the view format here and what's cool is by using this particular class the params map is going to map directly to the app label and the model name again looking at the permissions itself if you look at the params map you see right in here how all of that's happening which is pretty cool it's always nice to review the code that other people have written because you're going to learn a lot now these two options perhaps we want to have those permissions in there as well as such as view in this case i'm going to leave them open it's completely okay at this point okay so now that we've got this we're going to go and save it save everything i'll go back in here i'm going to get rid of all of my permissions first okay so i've saved the permissions now this person should be permissionless refresh permissionless great now i'm going to go ahead and add in the list view so products and view so the view product will work on a list as well as a detail uh if that wasn't clear already we refresh it here what do you know there it is and now of course if i make them a staff product editor which has all of the permissions i believe we're going to go ahead and save that we refresh in here and now you can do everything you need to including create so the last thing is of course just checking or verifying staff status let's go ahead and say that go back over to our list view here i refresh i don't have permission to perform this action now if i go and look at the admin itself it also seems like i'm logged out i'm actually not logged out i just don't have permission to go to the admin any longer so that's the staff part right interesting so what do i need to do then to augment this a little bit more so it's not that i'm putting all of the permissions i might need into my custom permission well to do that then i'm going to actually comment this part out only change this or really augment it only slightly that's all i'm going to change then in my actual view this is where i want to declare the actual permission itself so if i do permissions dot is admin user then is staff editor permission then it will do the same thing so if i come back in here make them staff again save it refresh in here what do you know cool so the ordering of this matters as well so what you're going to want to do is what permission you want to match first should go first so in this case i definitely want to make sure that they're an admin user first before anything else so it's very similar to like the actual permission itself so if if we look at this has permission here if i put a permission up here if not request by user dot let's say username is equal to cfe then return false this will basically always return false then if it's not the cfe user and so that ordering also matters of course so that logic matters as well on our actual view class and that's just something you can play around with as far as the ordering is concerned the authentication classes is not as strict you can use different authentication classes as well which is also pretty nice okay so that's custom permissions now the big thing here going forward what we need to do is we actually need to update our defaults as well as potentially introduce a new type of authentication not potentially we definitely have to introduce a nice new type of authentication because our poor old pi client here does not have access to listing things out now right so if we do pi font and pi client and list stop hi i still get nothing because it's not authenticated so that is another thing that we need to solve if you have questions on the custom permissions let me know like i said before a lot of the built-in permissions are going to cover the gambit of what you'll probably actually need every once in a while though maybe you have something that's a member like a user that's a member that is where you could put has permission in there is staff is sort of like is member but it's not quite the same because maybe you have a completely different model that covers the membership portion of your site as well that maybe adds a layer of payment or some other reason to be a member cool so yeah that's permissions now let's go ahead and take a look at the token authentication all right so now we're going to go ahead and implement token authentication so our python client can actually talk to our backend or the rest api we've created so the first thing i need to do is jump into cfe home into settings into installed apps right underneath the rest framework itself we're going to go ahead and do dot auth token once we do that let's go ahead and make sure we run our migrations so into our back end we'll run python manage.py migrate okay so let's go ahead and cd back into the root so i can run pi client commands in a moment now we can verify that this now has tokens by of course logging in i see that i've got my auth tokens here and i can create tokens for any given user so what one of the things i want to do with this is i want to be able to create these tokens at any time but also be able to delete them these tokens when it calls for it so to actually create the tokens it's also pretty simple let's actually jump into our api urls here and i'm going to go ahead and come in and do from rest framework dot off token dot views we're going to import obtain the auth token so what i'm doing here is i'm creating an endpoint to actually generate these auth tokens and so to do this i'll go ahead and do path and it's going to be auth slash and just simply that view that's it okay so now what we want to do is take a look at our protected views so in the case of our views.pi for our product list create view we do not have the correct authentication classes we don't have all of them at least so i'm going to go ahead and add one more and that's going to be authentication and dot is going to be token authentication so now i can actually use that token authentication to work with the product list create api view so if i run python pi client and list dot pi of course it's still not working so back in to list dot pi what i'm going to do here is i'm going to create a new call so right above here i'll go ahead and do the auth response this is going to be post here the endpoint itself is api off like we just implemented right here okay and we want to pass in some data here the data we're going to pass in is the username which in my case is cfe and then our password so whatever this password ends up being so what i don't want to do on the regular is to pass in raw passwords in here right i want to pass in a secure password as in i want to actually have this based off of an input of some kind so how do we go about doing that that's pretty simple we just run from get pass import git pass and so now our password is going to be based off of that and then we'll pass it in here so the end point here i'll call this my actual off endpoint and there we go okay so let's go ahead and see what that response ends up being i'm going to run this list again it's asking for my password and it gives me this response here if i do it again again it's going to give me a token okay so these tokens are identical so this is good and bad right but for now i'm just going to leave it in we're not going to talk too much about making better tokens because there's a lot of different packages and how we can improve things but for now i'll just stick with this particular token and we're going to assume basically if the auth response dot status code is equal to 200 which is what it appears to be right there then we're going to go ahead and do this request so let's bring this back i'm also going to go ahead and say that token is equal to the response token and that of course is this data right here next what i'm going to do is say headers equals to and we want to do the authorization header and in this case it's just going to be simply token and it's going to take in this token so we use an f string here and bring that token in just like that with these headers i can pass it in to the request itself just like that so now this is a little bit closer to how we might interact with this realistically though we would actually store the token somewhere and have the ability to reuse it i'm just going to make this where i run it each time and it logs me in okay so i gave the wrong credentials that time and i did it again and there we go okay so i did the right credentials and now it's actually sending this stuff back so literally every time i run this i have to enter my credentials and it runs it back for me pretty awesome now i can also do the username of course so we'll go ahead and say username equals to input what is your username and like that i think can get password we can say what is your password and something along those lines and then we come in here to our username and now i can do the full thing username and then my login and there it goes great so that is now how i can actually authenticate with a token now there is one other aspect of this that i might want to actually do and that is having a different keyword like instead of it saying token here perhaps i want it to say bearer this is very common with authorization headers is having bearer not necessarily token so to do this it's also fairly straightforward this time i'm going to go ahead and go into the api and create authentication dot pi and now i'm going to go ahead and do from rest framework dot authentication i'm going to import the token authentication and i'll say as the base token off and then i'll do it create a new class just call it the token authentication so essentially overriding the default one and then we'll go ahead and set the keyword to bearer and now i have overwritten that token i just need to bring it in here now so then we'll do from api dot or yeah from api we'll import our authentication actually we'll import the authentication itself so from api dot authentication we're going to import that token authentication now and we'll get rid of that other one save it now back in list we have it as bearer we'll run it again and again i'll put in my username and password and again it works this time it was just a different header cool so this is actually i think pretty clear in the documentation on how this ends up happening how you can actually make this work now some things to consider with tokens of any kind is to delete them after a certain amount of time so one way to delete them is quite literally go in here and just say delete right that will actually force a new token to be created and force the user to log in again right so the way i can actually show this is by let's actually save that token i print it out somewhere in here so here's this token right here i'll go ahead and copy that and now we'll go ahead and put the raw token in just to see what happens with the create method here so here's my token i'll go and say headers equals to again authorization and i'll just go ahead and write in bearer now okay so i've got my headers let's go ahead and add in our headers here there we go let's give it a shot with create.pi hit enter the authentication credentials are incorrect let's spell bearer correctly let's try that again and there we go so it actually does create them and if i delete this token just like that it's kind of like logging them out invalid token right so to require them to log back in is kind of the idea there so that is just a really simple implementation of creating tokens and headers and whatnot so one of the things that you could do as far as getting rid of tokens is deleting them after a certain amount of time let's actually see what it looks like as far as storage goes this is going to create a new token for me and i'll log in there we go so let's go ahead and jump into the shell and oops we need to jump into the back end and then python manage why shell and now what i want to do is grab that actual token model itself so from rest framework we're going to go ahead and grab in the auth token.models import all and if i look at locals i can see all of the different things that are coming in here it should be something related to oh that's way too much stuff but anyways the model token are the token itself as we got there and if we do token.objects at all i see all the tokens i do the first one and now i'm going to go ahead and do what's in that token itself and we've got a lot of stuff going on here right so um what we kind of want to see here is essentially when this created so we've got a created date here so now what i can do let's go ahead and grab my token object by saying token.objects.first token object.created now i can see when it's created right so what this implies then is i can actually clear these out every day with like a cron job or something like that or even using celery to clear it out as well that of course is going down a line of thinking that you probably should do with tokens like this is to clear out the token and make sure that they're refreshing their session but a lot of times you actually could let the user decide this stuff if you think about whenever you've worked with a api service sometimes they'll enforce an expiration time sometimes they will not enforce that and so in this case it doesn't necessarily enforce it by default there is no expiration in here by default and i think that's one of the coolest things about the django rest framework is there's a lot of other authentication packages that you can use including creating what do you know your own custom authentication class so you can absolutely start to be smart with how you build these things and this token model i mean you could just look at the code itself to really review what you might want to change in it so to do to actually review that you would come in here and do from rest framework and then dot off token dot models import the token class itself now go to the definition and you can see just how simple the actual model is itself it's not really that complicated you could quite literally copy this and create another field on here called expires that's essentially the same thing as this that is just a little bit in the future right so when you actually save it so if it's the key is not generated right here you would then say something like self.expires and then set that value of course not in the actual package itself but on your own code if you are interested in doing something along those lines now that kind of customization is not something you'll necessarily always need to do because there are third-party packages that also exist to help make these things happen as you can experiment with on your own now the thing about the authentication packages themselves all of these other third-party ones do rotate they change from time to time and since authenticating is a security issue and these are also production ready the idea here is well sometimes security best practices change these 30 party packages don't always change with it necessarily i i can't say for sure is the point right and that's also true with these other authentication methods but these are a little bit more well-worn paths how you do these things securely is like a whole topic we could talk about for hours and hours and hours but just the general rule of thumb here is you probably want to roll these tokens in some sort of fashion whether it's going directly from the created date perhaps they roll every three days and then they have to create a new one or every month and it also depends on also what these tokens are getting access to right so in the case of just receiving a bunch of data you probably don't need to change these tokens very often right because they're not actually making any changes to the back end so that is another thing to sort of think about of course if you have questions on tokens on authentication let me know otherwise let's keep going so in an effort to simplify our code we want to actually leverage the rest framework settings now the reason we're leveraging this has to do with the authentication classes and the permission classes in this part realistically we just want to set some defaults there so that no matter what our view is it goes off of those defaults and it's more important for authentication and permission potentially throttling than pretty much anything else so let's take a look at how we do that first off go into the rest framework documentation scroll down to settings and we just want to grab the rest framework dictionary here you can absolutely use these default renderer and parser classes by all means check out those on your own but for now we're going to go ahead and bring in our default authentication class so this right here i'm going to go ahead and add in this key value as an empty list and then our default permission classes and also as an empty list so what is it that we want to do here now the permission classes and the authentication classes i think should be really clear at this point because of what we did in our views we used authentication classes and permission classes so the biggest question is what do we want our default for all of our views to be now in my opinion for the authentication classes i don't actually want to rewrite these over and over again i'm pretty much going to set my authentication classes for my project and then just change it as needed on any given view more on that in just a moment but these are the ones i'm going to use so to set them on our settings we don't actually import the module itself instead we declare a path to that so in other words we use restframework.authentication right so this is the rest framework package the authentication module and then we do the authentication we want to use so let's say for instance session authentication okay and then in our actual authentication class these token authentication class that we customized a little bit we can also declare that one so api.authentication.token authentication and the reason i actually set it up this way so that if i didn't want to change back to the default rest api or the rest framework one i would just do it like that but i'm gonna leave it in as just simply api authentication now the default permission classes this is gonna be one that we really want to consider for our apis now in my case what i'm going to do is not the same as what we have on our list create api view instead i'm just going to have the is authenticated or read-only right so come in here and do rest framework dot authentication that is authenticated or read only so that means then any data that's going to be posted to my back end that user is going to be authenticated this pretty much allows only git to use on everyone and of course they're authenticated against these authentication classes so the other part of this that you can consider doing is saying that my auth classes maybe we put them equal to this and then if debug i can say my auth classes are well literally just the token authentication you know something like this and then i can set that based off of that right and this is what i do often to test the configuration from time to time now when we go into production the way i use production is using different modules different actual settings modules for the production environment it's going to be very similar to my local environment but it's going to be slightly different to make sure things are locked down and secure in the way i want but the nice thing about having settings.pi in this way is we can actually use that debug mode right so this that should not be on in production we can actually use it to our advantage in relation to the django rest framework as well just like all of the other settings that are in settings up high but that was just a quick way to look at it but now that we have this we have a new thought that comes to mind and that is do i need this authentication classes any longer on my view and the answer is no in this view in particular i only need to have these permission classes and that's it i do not need to add any additional items to this because these are the permissions i want specifically for this api view now if i just wanted all authenticated users to have access to this boom there i've got now my authenticated users and they all have access to this so that's also pretty cool and this permission the product detail api view you know perhaps i want these same permission classes here as well and perhaps i want them all across the board on this particular set of api views now right away you might be like okay well i'm kind of repeating myself and i don't have it in a really let's say for instance a really non-redundant way like am i really going to copy and paste this everywhere i want my staff editor permission to be used and the answer of course is no so in the next one what we're going to do is actually cover mix-ins so that this is not used over and over again but rather a mix-in is used on our behalf a permission mix-in if you will and so that is actually how you solve that problem and so how you solve the problem for a lot of different things as we'll see but the idea here now is we do have a default across the board for authentication and permission and then if we need to override or change that default we can do it right on the view itself at the end of the day whatever the view says is what's going to be the last line of defense right so if the view is empty for example it's an empty list here we just cut off all of our permissions right so that is a good or bad thing potentially depending on how it is and that's going to be true for authentication classes as well so if this is not clear at all please let me know hopefully it is hopefully it becomes even more clear when we start using mix-ins but having these defaults are good for all sorts of things and it's also a good place to test out what ends up happening like a throttle class what's a throttle class what is throttling exactly well throttling in this case if you look through it it's actually just a way to limit the number of requests given a time period second minute hour day so that means that one user let's say for instance a logged in user can only do a thousand requests in a day now i don't think the django rest framework is necessarily the best place to have your throttles for the api lookups i actually think it might be a good idea to consider using something like nginx where we have additional configuration controls for throttling on our whole project not just the api right this is not going to care at all if they're going to you know your local host over and over and over again unless of course that is an api too so yeah and it looks like we have an issue here that we've got in our settings i'm glad this came up because i wrote authentication that should say permissions okay and there we go that solves that problem cool so if you have any questions on the settings or configuration of course let me know i think the documentation does lay this out really well in the places that have default configuration for the rest framework all right so now i want to simplify my permission classes by creating a mix-in now let's go ahead and do it first so the idea here is i want to take this permissions.pi from products app and bring it into my apis app and i absolutely do want to move it and luckily vs code will refactor some things for me which is really just this right here it's not anywhere else but i want to actually now use this staff editor permission just inside the api module itself or this folder here now the reason for this is because is staff editor permission i'm probably going to use this all throughout my site maybe not but i want to actually at least set myself up for success there so inside of this api module i'll also create a mix-in so we'll call it mix-ins.pi this bix-in is going to take in this permission so we'll go ahead and do from dot permissions we're going to import that permission and i'll just call this my staff editor mix-in let's go ahead and just say staff editor permission mix in just so i know that it is related to the permissions themselves so again i'll do my permission classes and it's going to be equal to that staff editor permission again we also want to go from rest framework we're going to go ahead and import our permissions here as well and then i'll go ahead and just do permissions dot is admin user and staff editor permission now that i have this mix in i can just reuse it in any view that needs it any view that allows for this permission classes argument to be added so back into my products views here we're going to go ahead and import that so from api.mixins we're going to import the staff editor permission mix in and i'll just add it into the different views that need it and we can do it just like that now i no longer need that permission classes it's actually declared right here and right here okay and we can just repeat that process for each view itself now the reason i'm actually separating the lines like that is just for cleanliness purposes and also pep 8 just to make sure that they are not too long of lines themselves if i do it correctly and there we go okay and final one is right here i'm not sure why it's doing it like that but let's go ahead and do that and there we go okay so now i've got that staff editor permission i can get rid of all these permission classes and i can get rid of all of the unnecessary imports that i probably have here now which are going to be you know kind of less color here just like that i don't need the authentication one i have that in by default i don't need this anymore and so now it's just a little bit cleaner of views and granted i have it say staff editor on there so hopefully that's really clear to me what's going on of course i could always just go to that definition and see it right there and also see the permission itself and that gives me exactly what i needed and so we can test this out again so my user is logged in they don't have permission to view or edit anything we go back into the super user and add in those permissions let's say staff product editor group we save that and we refresh in here and what do you know it actually gives me all of those permissions back in both places and so that mix-in is just a simple and easy way to do this now the question is can i use mix-ins for other things well absolutely you can use mix-ins for your query set you can use it for your serializer class they are a nice way to shortcut rewriting a lot of code over and over and over again right now in the case of these views i probably won't actually use this staff editor permission in the future which is kind of funny but the idea though overall is that we do now have this mix in and it's a really easy thing to just add on any view that we want now of course writing out the permission classes themselves is not that hard to do and it's also pretty easy to remember maybe overall but the general reason for this is just to be able to really simply and easily add the permissions that we might need especially if you want to make a longer list of them because the other thing about this now is maybe i'm like oh i don't want it to be admin user anymore i'm going to change it to being another custom one that's saying that the username contains let's say for instance i create a new permission called username contains cfe permission or something like that now that's the only staff members that have access to this right and this is where you could change it on the fly and now it affects all of the views that actually use that permission which of course has serious implications as well because if you make a mistake and maybe you say just allow any you know so like permissions.allow any well that's a problem as well so it is a convenience but it also potentially can leave you vulnerable to mistakes like that but overall i think this is a little bit easier of a way to just use some permissions but also a way to see how mix-ins work let's take a look at the django rest framework view sets and routers they are pretty interesting and let's see why so inside of the products app here i'm going to make a new module called view sets dot pi typically speaking you would actually put your view sets in views.pi but views.pi is already pretty full so we're going to leave it in viewsets.pi so we're going to go ahead and do from rest framework we're going to go and import our view sets and then i'll go ahead and do from.models we're going to import our product model and then from.serializers we're going to go and import our product serializer and then we'll go ahead and do class product and view set and it's a view sets dot view or rather model view set and we declare our query set first and product.objects.all then our serializer class and this of course is going to be our product serializer now this should already look kind of familiar to you like a you know generic api view that we've already seen let's say for instance our product list create view if i break it down a little bit we just did these two lines these two lines interesting so that's actually pretty cool so we now have a very similar format to something that we've already seen before i'm also going to go ahead and put in our lookup field here and leave it in as just pk that is of course the default but now i've got a view set let's actually put it into routing so we can have a better sense as to what's going on so in the root configuration of our django project i'm going to go ahead and create a file called routers.pi and i'm going to go ahead and create our first routing now overall i actually would not create routers.pi i would put everything in urls up high but i really wanted to separate out the view sets and routers specifically as we'll discuss so the first thing that we want to do is from a rest framework dot routers we're gonna import our default router and i'm gonna go ahead and do from dot products dot view sets we're gonna import our product view set all right so now i'm going to declare my router and it's going to be our default router here an instance of it and then we just want to do router.register and in this case we'll go ahead and say products actually let's do products dash abc just for illustration purposes then we use our product view set here and we'll go ahead and add in a base name of just simply products okay so we now have a router here let's go into our urls this is again our main configuration urls here and what i actually want to do is i want to update so that my router urls are actually being used in here so back into my routers then i actually am going to declare url patterns equals to router.urls okay so if we print this statement out we'll see what that ends up looking like but let's go ahead and do that it will be a list of url patterns which is why i declared this variable here but now i actually want to do something very similar to this products urls here i'm going to go ahead and copy this and paste right underneath this time we do api v2 right so this is my version 2 of an api and this of course is going to be simply in our routers.pi so so cfe home dot routers okay so we're going to save that and run our server just make sure that the server is still running and what we want to do now is take a look at this routing so we're going back into our url here and i want to go into api v2 and hit enter so api v2 i went to the root of that and what do you know i actually have products dash abc and it goes into api v2 products abc if i click on that what i see here looks like maybe a misspelled serializer class let's go back into our view set itself and sure enough i did there we go save that refresh hey what do you know i've got product list in here and i can even create something again i'm not in the other products urls i'm in v2 right and so the routing here then is well this is the prepend path this is the root path that's going to be right here and then if we wanted to see individual instances we need to change our serializer a little bit so let's go back into our serializer i'm going to go ahead and add in the pk value here i'm going to save that and refresh into our list view and what we see is the primary key so now i can actually more easily go to that detail view hit enter and what do you know i see product instance it's actually giving me things about this particular product again this is all coming from the view set this you know few lines of code and this routers so granted i did print out these routers.url and in our running server we can see what that print statement does it's different url patterns that are actually coming through in here these url patterns are very similar to what we manually did in url supply like this they are different because of how the view sets actually route every kind of request but they're not that much different so if we think of a product view set what it ends up doing is we have a get method to get the list of items which is you know just the query set and then we also have a get method to get the the retrieve a item which is of course the product instance like detail view if you will we also have something to post an item this of course is to create something so new instance we also have put which is to update we have patch which is to partial update we have delete which is or rather destroy now delete which is to destroy right so the http method is delete these are all the hd methods and these are the actual sort of functions that are being called when we run them what do i mean by functions well all of this is very similar to what we did with our product mix in view we have these three mix-ins in this case but if i added additional mix-ins and updated the hdb methods accordingly then i would have my own view set that's essentially what's happening here then once i do have a view set i then bring it in as a router this is how i make all of the urls that it correspond to this view set now naturally we would probably put it in as products so that it's more closely related to our other api views right so there's v2 and if i just go into products there's v1 it's the same data overall it's just how the endpoints work are just slightly different so the reason i actually don't use view sets very often is because of how these routing ends up working router.url doesn't give me the granular control i personally like over my urls i like declaring exactly what my urls are and where they go now i realize some of you that end up using view sets a lot start to research more about it and understand that there is an actual pattern that you can know right off the bat on how they end up working but the thing is view sets have a lot of features that we can do with them which i'll show you in a moment so this routers here doesn't really show me anything about the urls themselves now you might be saying the same thing about this include thing here on either one but it actually does point to a place where actual urls are declared right here okay so now i want to show you why i really don't use view sets and it has to do with using a different kind of view set that sort of breaks the patterns if you will and so let's go ahead and create one i'm going to go ahead and call this my product and we'll just call this our product generic view set and this is going to take in view sets dot generic view set and again i will declare these items here but this time what i want to do is instead of all the crud methods i only want a few of them and that's maybe these two right so i'll go ahead and get rid of these four here now i just want to have support for these two now at this point it's a generic view set which the model view set does inherit from so just like our generic view this right here we can actually use mix-ins i'm going to use the list model mix-in and the retrieve model mix-in so again going back in here we'll go ahead and change this or import mix-ins rather and then just add in our mix-ins here and i'll tab them over and so there i've got my mix-ins now and we have a different view set i'll go ahead and go into my routers and just change my view set in here we'll save that now when i go to my products list i refresh in here it only has two supported endpoints that is products and the detail view so the list view and the detail view which you can actually see in the printed urls there are other other urls that are actually in there like the api root but that's more related to the router itself not the generic view sets but everything that's related to the generic view sets has to do with this default router right so it's only those two endpoints and well what do you know without printing that out there's really no good way to see all of the urls that are currently being supported by whatever this generic view set is right so yes there is yet another way to help clarify what that would end up being by turning a view set then into actual uh you know actual variables themselves by saying something like product list is this generic view set dot as view and we would say something like get and list right so this is now that product list and then i could do product or product let's call product list view and then i could do product detail view very similar and then this one would just be retrieve and now i can actually use these as view functions very similar to my urls like i did here in fact that's actually part of the reason i wanted to show you how to do these as view some people end up doing this not just in generic api views or any of these kind of views that we've used as well as also in view sets cool so like i said personally i really like using views.pi and how i did it all these generic views so that i have a very granular insight into how my urls are routed that doesn't mean you can't use view sets and you can't customize them to work really really well because you totally can and as you get more and more experienced perhaps using view sets will be the method that you end up going about it i personally don't end up using them very often but there are still really interesting ideas and something that might be well researching a little bit more okay so the other part of all of this is actually how we're serializing this data because on one hand our code maybe doesn't give us the granular insight into any given url but the data that's being serialized as in like our list views either one needs to actually show us maybe an endpoint an actual url that i can use to review this data so there's a number of things that we want to do with serializers and that will come very soon now we're going to go ahead and add in urls to our serializer so that we can actually get the detailed view or any of the other views we need inside of our product list view so let's go ahead and take a look at how to do this fairly simply within our product serializer the first way the wrong way i would do is by copying my discount go ahead and create it as a new field and we'll just call it url and then i will create the serializer method itself with url obj and this is just going to return the path to it so api products and obj.id or really obj.pk and hit slash okay so if i refresh in here there we go i've got a path now that brings me to where that end up being and of course if you had the correct you know base url you could use that path and that will give you the detail view cool but this of course is flawed because if i change all of my api to the version 2 that view sets thing then well this would have to be manually updated of course with one serializer one model it's not really that big a video to change it like this but most projects don't have one serializer in one model they have a lot of serializers and a lot of models so what we want to do then is change this get url method to be well a little bit more effective so to do that we can go ahead and say from a rest underscore framework and import reverse and what this will allow me to do is basically make this same line here but do it using a command called reverse now this command is actually a little bit different than the default django one so what we want to do first is actually get the request from this serializer so self.context.get this request now why is it that it's a get method and it's not something like just simply you know self dot request well this has to do with how serializers can work they don't always have the request sometimes they will sometimes they won't it really depends on the context that you give it now luckily the context we're using inside of generic views they will have the request so all of the contacts we've done so far will have the request it's more like when you actually start using manual things to declare serializers like this right here or this right here that's when you'll start to need to add the request as a additional argument but since i have this request i can say if request is none then i'll just return you know maybe none we'll just return that we don't know what the url is but of course if the request is not none we can use it as an argument so we'll put it in here as an argument i also need to declare the actual view name and the keyword args that i'm going to be using so the keyword args are going to be our primary key and that is going to be of course our obj.pk okay so let me break this down a little bit what are these keyword arguments related to and what is it that i'm reversing well really i'm going into the urls these url patterns here and i'm reversing this pattern right here so pk is the name of the keyword argument uh what do you know keyword argument and so i just need to give this path a name as well so i'll go ahead and name it product detail this is the common way to name it the model name lowercase dash detail for a detail view for the list view it's going to be more like name product list shocking now the update and destroy view those aren't always going to be their own endpoints a lot of times they are actually baked into the product detail view which you totally could have yourself we're not going to worry about that right now instead we're going to worry about this one single url and now i've got the product detail in here so i can save that and we can look at our api again and we got a model module object is not callable and made a little mistake here this should be from a rest framework dot reverse import reverse save that and refresh again there we go now it's not just a path but it's an absolute url it's the entire url that goes there and of course if we click on it it'll bring me to that detail and this is true for any of these as we see here so that is another way to do it and you betcha there is yet a another way to do it so what i actually do is i'll change this one to just being called get edit url and we will change the product detail to product edit okay go back into our urls and name product edit all right there we go and so i'll call this my edit url now and this my edit url save it and of course that all works maybe let's try that again looks like i might have to restart my server and there we go okay so now i have an edit url let's go ahead and do the final method which is the preferred method and that is by saying url equals to serializers dot hyperlinked identity field this is going to be our view name now this is product detail hey what do you know view name we had the view name right here too interesting so i'll go ahead and separate this out a little bit next we can do our lookup field and this of course is going to be our primary key now the hyperlinked identity field only works on a serial model serializer it's like a shortcut to doing it where a serializer method field works anywhere in fact you can also then have all of these different urls running from that and then of course once we declare a field up here we actually need to add it in to our fields right there which is what this error is and we refresh in here now we've got that url so that's one way or well three ways on how we can actually declare these various urls now every once in a while you may or may not want to have these urls show up so perhaps the detail view you don't want the urls showing up you just want some additional context or less context in there and that's when you create a new serializer for that particular view right so go back into the you know detail view here we would just change the serializer to being the product detail serializer now this is also pretty interesting too because a detail view might have well a lot more detail it might have a lot more fields so i'll leave it up to you on how you want to go about changing that but now what we want to do is i just want to verify that since i added these two fields that it's not going to cause an issue for me when i create something so going back into the list view if i scroll down to the bottom there's no url field here so let me just go ahead and say hello world again again or again gain and hit post and sure enough it does create it for me and those urls are in there as in i didn't have any errors because i didn't also provide a url field we still need to see what does it look like if i have an additional field that's required that perhaps maybe isn't in the product model itself let's take a look at that in the next here is a very hypothetical situation that potentially could happen that is what if every time you create a new product you want to have a one-off email sent to somebody so in your product serializer you decide that hey let's go ahead and do email equals to serializers.email field and we can pass in this thing called write only write only of course means that it will literally only write to it versus read only we'll only read it now if i don't put write only in there and let's put this in our product fields list here and we refresh in products i get this error because that field does not exist on the product instance so changing it to write only presents a pretty cool unique opportunity and that it says hey it's no longer showing up so if i scroll down to the actual html form on the browseable api i see that email is now in here and of course if i try to post data i get an error it says this field cannot be blank hmm interesting so let's go ahead and send our pretend email to somebody we're saying hey we're going to add this and i'm going to go ahead and add an email and say hello there abc person exclamation mark or hello there random person you know whatever i'm gonna go ahead and hit post and let me try that again oh what the heck is this gotta type error when calling product.objects.create now of course the thing about a product serializer is it will take this data and then create it in the product model itself so what we can do is we can actually see how this is done in a serializer by overriding the default method that default method is simply create it takes in self and validated data and what it does by default assuming this data is validated we'll talk about validation in a little bit but we've already seen it we just saw it actually and that is when we went to post this this is validating that data so this create method will take in that validated data and then you can actually imagine it going you know product and then dot objects dot create and unpacking that validated data this is what it's doing by default for this instance now another way to write that same thing is just to return back the default value or what the inherited class is the model serializer what that actually does as in validated data and so i can absolutely run something like this let's go ahead and take a look at this a little bit i'm going to just call it an object and i'll return back object now i still have that email field in here so let's go ahead and pop that thing out i'll go ahead and say email equals to validateddata.pop email and i'll go ahead and print out email and obj we'll save it and we'll have a look in our products app again we'll go ahead and run this abc at whatever it doesn't actually matter just has to be somewhat of a valid email format a title and post hey what do you know it actually created it interesting and if we look in here we've got a product object and the email so what do you know i could now actually send that email in here now that was merely meant as a somewhat arbitrary practical example this is not the only place we could do this of course right so we could actually do it in the perform create method as well and just make sure that when we are calling serializer.save it's doing something very similar i actually want to see and test that assumption so i'm going to go ahead and get rid of this validate data part and this email part leave it sort of as to what it was now what i'm going to do is come back into our serializer here and say email equals to serializer.validateddata.pop okay let's see if this works on the view itself not on the serializer anymore but just the view so come back in here let's go ahead and create one on the list create view of course abc at whatever dot com some random title i hit post and what do you know it still works so that's because of this save method here serializer.save is being called well in two places in the view itself calls serializer.save serializer.save if there isn't an instance we'll run this create method if there is an instance it's going to run another method called update update which takes in self the actual instance itself and validated data and that of course will update the instance itself so it do something like instance.title equals to validated data or let's spell that correctly validateddata.get title and then it's just going to return the instance like that so that's the default you don't actually have to call save again it will save it for us which is pretty interesting so in other words serializer.save is very similar to like form.save or model.save those work very very similar if you're familiar with those in django and hopefully you're at least a little familiar and if you're not just realize that we can run the instance itself we can update it we can do all sorts of things in here especially when it comes to this content right so on the list create api view sure i actually added in a default value for this validated data this actually would probably be better served in the serializer that i use when i run the create method as in it would probably be better in here that's not something i want to worry about right now but there is one thing that i wanted to show you which is related to the update method for that email if for some reason you have this kind of silly contrived example going you would then run that same validate data pop and then you could update instance data if you want to or you could just run the super method and say update instance and validated data this will just ensure that that email gets popped out of here if you end up using it okay so this was really meant as an example not something that's really that practical but an example of two things one some arbitrary field that you might want and of course it would be then in your list of fields here and then of course the other part really diving into the create and update methods in any given serializer and the validated data of course is the data that's being sent to the serializer to be cleaned validated and then the instance or the actual model itself will be called as needed so of course if you have questions on this let me know this is going to be something that you're going to want to play around with on your own and realize it does work in conjunction with this perform crate on any given view realistically you might be wondering what would i do if i was running this perform create method i actually would pretty much very rarely change this perform create this i don't often have to change it's usually done in the serializer itself because it can also get the request method in that serializer too so we'll probably revisit those methods again but for now we're just going to leave it as is so you have a better understanding of create an update and also adding additional fields all right so now we're going to go ahead and look at custom validation with our serializers it's pretty straightforward but we're going to look at a few different ways and approaches on how you could do it now the first approach the simplest approach i think is to go directly into any kind of serializer whether it's a model serializer or just a standard django rs framework serializer to do this we pick the field that we want to validate our data from right so to do this we just come in here and we define validate and then the field name whatever that field name is so in our case let's say for instance our title here i'm going to go ahead and say validate title this takes in self and value and then we want it to return the value so this is the actual past value in here the value that you know is being submitted when we use this model serializer whether it's the create method or the update method that's kind of the idea it's not for the read only right it's not for the reading side of things it's only for when we are actually needing to validate the data clean it up and make sure it's okay to save that's the idea so in here with our validate underscore sum field title we can do all sorts of interesting things here so the first one maybe this is what you would do is actually saying product dot objects dot filter title underscore exact equals to that value so this is gonna look a query set up in our database to see if this title that i'm passing in exists on my project somewhere so if we say if qs dot exists then we'll go ahead and raise serializers dot validation error this or let's say for instance use the f string here so value is already a product name simple enough so we're going to go ahead and save that let's make sure that our server is running still sure enough it is and we can validate it now in our products list create let's go ahead and create one with hello world and hit post this time it created it right so there's a issue that we'll talk about in a second but let's go ahead and create another one and hit post this time it says hello world is already a product name now if we go back into our list here we have other ones that have hello world in here and also well this one has an exclamation mark right so it does have hello world sort of existing but look at this we've got hello world now this is where knowing query set lookups is really really important so if you do i exact that's case insensitive so now if we come back in here and say hello world and i'll do an exclamation mark hit post now i've already got that product name and so if we look for that hello world with an exclamation mark on a chrome search the you know casein doesn't matter but inside of our query set it does so yeah we would definitely want to have the i exact in here most likely you don't always have to do that but i think it's a good way to validate the title to just ensure that it's unique okay so another way to do this is let's go ahead and comment this out i'm actually going to copy it i'm going to create a another module here called validators.pie now validators are useful in many contexts not just the like validators for the django rest framework but also you can create validators directly for a model you can create validators for model forms as well but in this case i'm going to just validate this title and use it only for the rest framework so what i want to do is i want to import the serializer so from rest framework we're going to go and import our serializers and then we'll also do from now models import product okay so this is the same idea here we don't need this anymore we now have this same concept but then going back into my serializer the way i apply it on one hand i could absolutely apply it by putting it in this method here or what i could do is i could declare title equals to serializers dot character field and add in validators a list of validators that i'm going to use okay so then now i can go from dot validators we can import our new validator validate title okay so we save that now it's just changed the location a little bit so let's go ahead and run that again so hello world exclamation mark hit post and what do you know it's already a product name all right so that is two different methods on doing it one is an external one that we actually put into a title field now you're probably wondering wait a minute how did this end up happening we'll talk about that in a second then the other one is inline now most the time you'll probably do this right here or better yet you'll have it right on the model so if there's already a validator on the model then well you don't necessarily have to worry about creating additional validators for the django rest framework personally i put validators on models as much as possible especially with something that i want like the title to be unique but you know what every once in a while you might need it inside of the serializer itself because of the concept of request context so in the case of a request this is not something we'll actually end up doing but let's go ahead and just take a look at it self.context.git and the request now i can actually say my user equals to that request.user and if i did have a foreign key relationship in here i could now say user equals to that and this is actually another thing that i'll do a lot within validation which i think is also fairly important on how to execute things i mean realistically it's for that user right of course the validation still works but there is another method of validating a uniqueness and let's take a look at that now so what i'm going to do here is i'm actually going to call it just unique product title and what this does is we're going to go ahead and from the rest framework dot validators we're going to import the unique validator okay so the unique validator now we pass in a query set with this which is going to be product objects.all okay so now i have yet another one this one on the other hand now what i want to say is let's call this validate title no hello or something like that right so we'll go ahead and now say if hello in value dot lower this is when i'll raise this error and say something like hello is not allowed and you might imagine like you know this could also be an email extension and saying like oh these email extensions are not allowed and stuff like that so definitely play around with these values but the nice thing about this is i now have two validators that do somewhat practical things i suppose so in my serializers then of course i want to bring in the validate title no hello and i can put this in here and then i also want to bring in my unique product title okay save that oops let's make sure we're importing that in fact i'm going to change it to import the validators themselves and then we'll just do the dot notation okay so now we refresh in here i run this again and now i have actually two items that are coming through two types of validation again this is very very common on django models so you probably have seen this before maybe you have maybe have it but the other part of this is what we did right here okay so i actually re-declared what this title field should represent so when we look at the actual documentation we see all these different fields here right so let's say for instance i change this to being a email field saved that and then refresh in here and rent it again now it's giving me a new validation it's now validating whether or not this is an email address which of course our title we don't actually want it to be an email address but it's cool that we can change it on the fly now this is very very common is to change these things now another thing that you might end up doing let's say for instance so let's put it back to our character field here let's go ahead and say name and i'm going to go ahead and do serializers.char field again this time i'm going to give it a source this is going to be title and we'll call this read only being true so we save that and let's go ahead and go into the detail now and oops i forgot to add it into my fields here so the cool thing about the rest framework is if you declare fields here and especially in a model serializer you need to make sure that you also declare it in this list here it tells you that all the time and so i refresh in here now check this out so i now have two fields that are identical just with a different key value you know or a field name right so this is incredibly practical and all i did was use source to grab that title now the only reason i showed you this is mainly because we used this to override the defaults for our title so it's just a little dive into what else you can do with that the other cool thing is you can use foreign key relationship in here as well so if you did something like this and you had a user attached to the model which we don't then you could definitely do something like user.email pretty cool so that's custom validation with serializers definitely play around with it a lot more because you may or may not want to use these things from time to time now in my case i have this validate title this is an invalid item here so i don't want it any longer but it is something nice to know about because especially when you start having a bunch of users generating content for this rest api we want to actually start locking that down so let's talk about that in the next one now we're going to go ahead and update our product model so a user is actually attached to it so i'll go ahead and say user equals to models.foreign key and we want to of course use the user model so to do this we'll do from django.com we're going to import settings and we want to use user equals to settings.off user model this is really just ends up being a string to auth.user but this is the preferred method to do it especially because customizing the user model is not uncommon it's actually very well supported in django but we're going to use the default user model and in this case i definitely want to have it on here but i'm going to go ahead and declare the on delete method which is something you'll have to do now in this case i'll go ahead and say set null okay we could do set defaults because i actually want it to be my default user but i'm actually going to say that no user owns it if it is deleted or rather if the user itself is deleted i don't want to delete all the associated products so i'll go ahead and allow no being true but notice that i have blank being false and i still have a default so the default is going to be the very first user which of course assumes that i actually have the very first user which of course we do so i made some changes to my models so i definitely need to run python manage.py make migrations and python manage.py manage.by migrate okay no huge surprise there now with that let's go ahead and take a look at some of these models so inside of our admin if we go to any of these we should be able to see that now that user model is associated to my super user okay so i still have my staff user logged in here and they still have access to a lot of this stuff so what i want to do is enrich my serializer a little bit with that user so i'll go ahead and say user actually just into the fields i'll put it at the very top save that and refresh in here and so there's our user okay user one staff user is not user one let's just verify that inside of our views what i want to do is for the product list create view this is where i'm going to go ahead and say define and we're going to go ahead and call git query set and it's going to take in self and what i want here is the request which is self.request on the view it's self.request on the serializer if the request is attached to it itself.context.getrequest just a slight difference but the view will definitely have the request the serializer may or may not so with this i can print out the request.user and for now i'll just go ahead and return the default git query set method here and whenever in doubt of the arguments that go into any given method instance method i always put in the args and keyword args and bring those down as well okay so at this point hopefully it'll just show me a requested user so i refresh on my staff user and then i look in here i see that it says staff great so what this means then is i can take the default query set which is going to go to this and i can filter that down so i can return qs.filter and request.user is of course going to be our user here so let me comment this out okay and so there's our query set we know that they're logged in because we have staff editor permission mix in which of course if we forgot what that was we could just go look at it is admin user these are the permissions in here and then of course the default would be the authentication but since we have those permission i think we can feel good that this view that we can definitely use this request user of course if we didn't we would do something more like this and say if user dot is authenticated then we would maybe return that filter or better yet if they're not authenticated then we could do product.objects and then none and that would return an empty list but it's still of that model class okay so now let's go ahead and take a look at what happens from this if i refresh in this product list create api i have nothing absolutely no products whatsoever this of course is on purpose right so we're actually seeing only the items that this staff user is a associated to right now in this case it is a staff user so you know perhaps we want to filter in a different way like perhaps that we want to see if they're a part of a certain group or something like that um that's getting a little bit more advanced than we need to realistically this is just about who actually owns the products in there or really the query set that's in there which i think is very clear now but of course we now have a new problem and that's this right here this user data i absolutely do not want that the other part is we also want to have make sure that when we are saving this data and it is there is a request user and we do have a user foreign key now what we can do is in this perform create method much like i mentioned up here we can now say user equals to self.request.user okay so now i've got that what i'm going to do is just really quickly on my serializers.pi i'm going to go ahead and comment out the user on here right since i am the user that owns this data like the query set that's associated now in my view is only directly to me so i don't actually need my own information on the serializer data data not exactly at least we will come back to that in the next one but for now now i've got this title here and i'll go ahead and say hello world my old friend exclamation mark hit post notice that it's not allowed i got hello is not allowed which is kind of funny so let's go ahead and look at our validators here this of course is not giving me my proper validation okay so let's try that again and again hello's not allowed great so the value validation is working so i'll go ahead and just say product abc and we'll hit submit and i'll hit it again must be unique great we're seeing some good stuff here let's go back into my other user and do the same thing and i'm going to call this product abc and hit post wait a minute it worked they're the same thing so what we have here is a problem with this particular product unique validator when we did it up here on our own we didn't have that problem so what we can do here is we can actually change the lookup to being i exact we can just change that right in that unique validator and refresh in here and now it's saying it must be unique but of course if i do a b and then lowercase c let's try that one because it already did create the other one and of course that's same unique as well and we can try this all again by you know deleting those last three products and saying i'm sure and try it again with either user there's the first user and then the second user i'll put it all lowercase and we run it again cool still unique so now we have our data associated to a particular user with any given query set so this lookup right here something that i personally like doing is using it as a mix in as well so instead of having as we see here we've got our i really it's just going to be this i don't i probably don't even need that but basically i would come in and i'm going to put it in my main mix sense here and i would say class user query set mix in and then define git query set and again we're going to go ahead and return the default so args and keyword args here so we can say the user field is user okay so the actual user field will end up doing so this means then that my data or let's say my lookup data is going to be equal to an empty dictionary so we'll do the lookup data and then this is going to be self.user field and this is going to be equals to self.request by user okay if you're not familiar with this method well this is maybe new to you and so now that we've got that let's go ahead and grab the default query set which is going to be super and we'll go ahead and just run git query set dot get query set and args keyword args and then we'll just return qs that filter and then unpack the lookup data based off of that user field that we added in here we'll save it as is now i'm going to go ahead and use this user query set mix-in query set like that actually that's usually how query sets written so back into our view i will import this one so let's go ahead and put it just like that and so here we go i'm going to bring it in right above this one it actually doesn't matter as long as it's be above this now what i can do is comment this out save that and we'll look at products list we don't need to submit the form data but we'll look at the list i'm getting none back so let's go ahead and verify our mix in here and first off we'll go ahead and print out our lookup data and then i'm going to go ahead and print out my query set and refresh in there and take a look so i've got a full query set in here that's coming through and i also have my user data okay so there's a good chance that this user has no data so go ahead and say product new post and we take a look again sure enough there it is great so if i wanted to change my user field in my view i can now come in here and say user field equals to let's say owner for example and i refresh in here and now it's saying it can't resolve that these are the only options that you have user being one of them so that's another reason to use mix-ins and to just have a better understanding of how all of this stuff works in concert together so now that i've got this user query set mix in i can use it anywhere else i need to have the query set narrowed down so i keep in that default query set on purpose so that i can go off of that essentially right so then in my mix in it's actually going to have some sort of default that it's grabbing and this lookup here something that it's doing is basically saying look up data is equal to the dictionary of whatever this field string ends up being so if it's owner it's going to be owner and then that's based off of the self.request.user and this is a quick and easy way to do that and then of course when you want to unpack it you just do this and that turns that self.user field equally to self.request.user as the actual filter in there so just a nice and cool way to do that and then of course back into our views everywhere we need to actually run a query set related item we could come in here and do that i won't worry about the product mix and view but here we go so now if i go into this products here i got product 34 staff user can see it this user cannot okay so this works somewhat in concert with the actual permissions so here's the biggest flaw as to how it currently works and this mix in it has to do with our actual permissions here so if we think back to our views if i wanted to have or let's actually think back to our permissions again is staff editor permission right so if they have permission to edit every product but our mixin on the other hand actually changes our query set then we kind of need to think differently about the query set itself so what i do then is basically if well let's go ahead and declare our user and this still is going to be self.request.user and now if the user.is staff i'm just going to return the query set and we'll set that equal to user okay so this will just return that default and this should say is admin rather this will return that default so if we refresh or no it says staff mistake okay so in the cfe user they can now see this stuff uh the owner can see it as well so it's kind of that combination right so again you could also do allow staff view being true or false and so then if self.allow stat view and user is staff then we can take a look at this and of course if i come through and just change this allow staff view on any given view let's go into the list view again to being false then it's gone so in my case i am going to actually come back to my mix in and keep that as false right i want to keep it as false because you want to have the least amount of permissions as default now this is just a simple way even though it might not feel simple it's a simple way to have a little bit more robustness in your system but by all means you can absolutely write get query set on the views that you need to based off of any given user but through my experience i just feel like only writing it one time and then using a mix in over and over wherever i need to narrow down a query set based off of the requested user this is a simple way to do that and i also wouldn't be surprised if there's a lot of other packages that potentially could do that as well for us but i'm not going to worry about that now i really just wanted to show you how to grab requested user data first and then how to make it a little bit more efficient overall now we're going to take a look at how to do a related field serialization or foreign key serialization now it starts with this we've got a product lookup the user can be attached to that lookup so if i look at this user there's that user and here's the product object right so those two have a relation now from the user i can also find all of the related products that are associated to that user so that's a reverse relationship now what we can do is we can actually well enrich our serializer data based off of this relationship we've already seen it basic right so if i do user like that i can refresh in here and i can see my user right on everyone every single one now has the user but more specifically it has the user id that's what it does by default now on one hand we could use the serializer method field right so if i came in here and said something like you know my user data and i went in and did serializer.serializer method field we'll go ahead and say read only being true that way we don't try to write this data and then i can use define you know get that user data get underscore whatever we named that field and it's going to be self and object i can now return let's say a dictionary based off of the user so let's say username is obj.username right and in fact i could do a lot of other stuff with this serializer method field we'll actually bring that back shortly but that of course is not what we want we don't actually want to use that field my user data at yet right and product has no attribute username this should be prop object.user that username but anyways so now we've got that and what do you know it shows this up now this is not the preferred method this is not the recommended method for serializing related field data instead what we can do is create a new serializer so in my api i'm going to go ahead and create a general user serializer in here so serializers.pi now generally speaking for user related things i would actually create an entire app that's dedicated to the user but i want to just simplify things and make it only in the api because what we're going to do here is incredibly simple so let's go ahead and do from rest framework we're going to go ahead and import serializer and then i'm going to just declare class user public serializer as in this is going to be public data that is serializer dot just serializer that's it that's all i actually need so i'll go ahead and say username equals to serializers.char field or character field read only being true and this should be serializers not serializer silly okay so we've got our username field here now let's bring in this serializer to my other one so we'll go ahead and do from api dot serializers import our user public serializer and with this i can now use it very much like i use all of these other fields i can go ahead and say user equals to user public serializer we initialize it with the parentheses let me just break this down so you can see how it relates and again we can declare read only being true now the reason that i have a read only here as well as here is because on the serializer itself i'm going to declare which things i want to be real read only and then when i reuse that serializer i also might want to declare which things are going to be read only but now that we have this if i refresh into any product itself i have more insight or more detail related to any particular user so naturally we can go even further than that i could go ahead and change this to being simply owner and then changing the source to user just like that so then i would come down here and change the field name to owner as well and that's a quick and easy way to just change who this is or what this represents even though i didn't change the model at all so it gives us a lot of flexibility in how we actually display this data right then i also mentioned this email field here you can also bring in user data this way as well so if you wanted to actually source out some data there we go in this case i don't actually have an email but that is a way to do it this should probably be email field not character field but that would be a way to show that up now of course i actually don't want to have user related data like this if i'm going to associate the user i would put all of that data in there serializer itself and more more realistically i probably won't ever put emails in a public serializer either so in any case let's go ahead and take a look at maybe some other field data we might want let's say for instance id and i'll go ahead and do serializers serializers.integer field and again read only being true we save that and we refresh in here and what do you know there's my id so this is a basic serializer notice that there's nothing model related so if i come into saying something like my serializer method field in here what's going to happen well let's go ahead and say related or other products for example right something like this serializer method field other products define get other products self and object and then we'll go ahead and return an empty list i want to just print out the object itself save that and refresh in here it shows other products it shows staff what do you know it's actually still working i have never imported the user model at all here it's still working so i can actually declare this the user is equal to that object so i can have products in here now and say something like the query set that i want is going to be product.objects.all or not no it's going to be all the related products so it'd be something like my products equals to user dot product set dot all this is how that foreign key relationship works right now you're like wait a minute i actually want to show my products here now you might be like oh i'm going to go ahead and import from products.serializers i'm going to import the product serializer now and now we're going down a rabbit hole that's not a good idea but i will explain how to do this so what i'm doing here is i'm importing the public serializer from api.serializers then in api serializers i'm trying to import that product serializer even if somehow i could do those imports correctly i have some conflicts with how they're imported there's a circular import here they're both importing the same thing but the other problem is that i have also a nested import so this user public serializer is in that product serializer that product serializer perhaps or that user public serializer maybe we want to use that product serializer that's of course not what i want to do so without getting too much of the weeds on that let's actually just create a class and i'll say user product inline serializer and this is going to be again serializer.serializer and then what do we want here well one is going to be our url and maybe our title so serializers.char field again read only being true and then read only being true so here's our user product inline serializer that is going to serialize this data in theory i'll explain why this is probably not a good practice but i will put it in here and i'm actually going to limit the number in here also up to maybe five so five related products so to use these things together what i could do is i can actually pass in that query set and also pass in the argument of many being true and then i can say data so this query set here now if i refresh on my product i get this extra thing here i need the request argument okay so i actually need the request in the serializer so this actual serializer doesn't work with my url identity field let me just get rid of that for a moment so i can at least show you that this part works okay so so far it's working let me bring this hyper link identity field back in and actually get that working as well if it's possible so we'll go ahead and say request equals to self.context.get the request there we go now to add that context in here it actually would probably just be easier to do context equals to self.context so it gets the same context as the user public serializer so we save that and refresh and what do you know now i have a nested nested serializer this is even more clear when we look at my other user and we can see each entry here has the owner and some other products it's pretty neat i think now this right here i don't think is actually that practical realistically um in this case right like if you're going to be looking at all of the products that you have why would you need to look at some of the other products that you have so in other words how our view is currently set up for this we have the user query set mix in that's narrowing down the query set based off of the user why would we then also need to see not only that yeah i'm the owner of this but also all this other data that potentially could come through that wasn't really the point of this one the point of this one was to show you how to bring in some nested data some nested serialized data and a lot of it right so on one hand this is these two things is probably a little bit more practical even if it's your data right so something like this that's that's probably very practical i would say you have your data in there and also if you changed it to where the permissions are different so other members of the staff can actually come in and see who's the owner now actually being able to serialize a lot of different data can be useful um and maybe in a way that is a view specifically dedicated to other kinds of products right but i really just want to show you that you can do it in here now the other thing is in our product serializer here you know perhaps we also want to do something very similar so i'm going to go ahead and grab this user product inline serializer and i'll paste this in here this time i'll go ahead and say product inline serializer very similar still and i'll go ahead and say related products equals to the product inline serializer now i'm going to try source being user.product again read only being true and we'll go ahead and pass in these related products down here as a field save that and we'll refresh in here and query set has no attribute pk one problem here is that i didn't pass in many being true as well so mini is always associated to a query set and i refresh in here now i have another way to see some of that data right of course it's not limited so there is some challenges to this and yet again we could do something very similar to this with that serializer method field in other words looking back to what we just did this can also be done on this other product serializer that it doesn't really matter just as long as you have a query set and then you use a serializer that can actually serialize that data now the only reason that product inline serializer and user public serializer can actually serialize this data is because i'm not using fields that don't exist so if i said this is not real and then try to serialize whatever this is not real is and refresh in here well that data should not actually come through at all right there's nothing for it to serialize it doesn't cause an error but there's nothing for it to serialize now if i wanted it to cause an error i would change it to a model serializer i would do from django.contrib.off i would import git user model and i would actually set the get user model and update this a little bit say class meta model equals to user fields are going to be these fields here so username and this is not real and then finally id we save that and we refresh in here and still not getting any issues okay so if i did try to have this as a write only field or right field as well there's a good chance that that will cause issues but overall the nice thing about this is it's not breaking things if i use a field that just simply doesn't exist but what we had here when it was just the serializer it still worked and it worked really well the only reason you want to use model serializers realistically or well maybe not the only reason but a really good reason to do it is if you're using create and update on those fields i usually default to using the model serializer because i don't know when i may use those update and create fields except for when it's like public data when i know for sure i'm definitely not going to be updating this public serializer this is just a lot cleaner very simple a lot easier to test and there we go so if you have any questions on using foreign key serialization let me know this was fast definitely fast as a section but i sort of assumed that you already had some experience with foreign keys so um a lot of the things that i kind of breezed through like the relatedness of these fields was because of that the other part of this is this is not a very good example to really look into how foreign key serialization works and you'll start to learn it a lot more as you actually apply it as you actually start using things a lot now the last real big challenge that we have with this current iteration is this related products thing is getting every single product so as your products grow it's going to grow it's going to grow a lot and it's not going to be good right so that's interesting as well whenever you end up using a query set or list view it's often a really good idea to use pagination that's what we're going to implement in this one across our entire project now let's go ahead and think through the logic here if you think about our list view it is narrowing down my query set it's making it a smaller query set because it's based off of the requested user that's what we did with this user query set mix in right here right that actually should filter down the data so it's not literally product dot objects you know.all right that's kind of the key it's not getting literally everything but this data can still be incredibly massive like if this user adds thousands and thousands of products we probably don't want to have to look them up every single time right so in this case i'm looking them up not only from the listview perspective but also in the serializer itself i'm also then looking up that same list which is nuts we should not be doing that at all so our first step to making this more efficient is to get rid of that related products inline that was meant to illustrate a purpose i don't need it anymore i probably don't need my user data anymore like i can really kind of hone in on these things my discount probably don't need that anymore either so let's go ahead and get rid of some of those things as well and also some of these comments here just to really focus in on our serializer if you look at different branches you can still see a lot of this old data that's in here if you need okay so now our serializer is a little bit more refined i refresh in here it's a much better query set overall because i'm not doing two of the exact same lookups anymore but it's still not great because it's not paginated so what we want to do then is i want to paginate all query sets all list view query sets basically to do this we just go into our settings module we scroll to the very bottom and we go into our rest framework here and we define our default pagination class and this is going to be let's put a comma right above it get rid of that red squiggly is just simply rest framework dot pagination dot limit offset pagination and then we'll go ahead and do our page size and set it to like let's set it to 10 okay let's make sure we have all of our commas correct and all that if for some reason you have issues spelling these things out or finding these packages whenever in doubt just go into the api guide go to pagination and you know copy this first item here or some of it at least okay so now we've got this page size of 10. now if i go into my list views here let's make sure it's actually running looks like it's not let's go back in here now what i've got is a different kind of look up it's just slightly different but it's still different it now has results it has a count as in the number of pages that are here and where we are currently this look up here is more efficient than well looking up everything and now it's actually going one by one and we can actually see various things that correspond to all of these lookups right and that's really the key here is i can actually paginate through and of course i could do this all with a api client of some kind like our python client and if we actually look at our python client now and try it so if i do python and pi client list dot pi username staff our password now it's actually showing me those same things right so the actual data is inside of results that's by default this will also show me how many pages there are which what this does is inform us how i could actually change my python client ever so slightly i'm actually not going to change it but i will mention how to do it so basically what you would do then is this response you get a next url in that response the json data that is so we get json data right so we do data and next which is what that would end up being and basically if next is not none then i could do another request to the next url then and then i would just keep that going i would run that recursively if i actually wanted to request every single piece of data and then of course my results would actually be in data and results and that would be or not requests but rather results and that would be the actual data that i might want to display or use right so that's the actual product data now and again if we do this now with a little bit more data we now see quite a bit in there and i also should see that next url coming through oops i didn't print it out it looks like so let's go ahead and print out that next url as well and we'll just say next url let's run it one more time with that user that has a bunch of urls if i scroll up a bit i should be able to see that it's now giving me that next you know endpoint which of course i could actually verify with my session authentication that i have running as well all right so that's pagination it's a very simple and easy way to break up our query set so it's a more efficient of a lookup and then we just use these parameters here to iterate through those iterations right so if you change the offset to maybe let's do it at 30 or 40 doing that offset means that well what do you know i actually don't have any data at this offset right which is also pretty interesting but if i change it back to like 20 or even changing this upset up here to like 14 right it will still do that and this limit here this this is the number that's going to be responded back to me so if i do 2 it will show me only two and then i'll have a number of pages that correspond to the limit and the offset the offset is like how many pages in advance based off of the limit right so if the limit's two and you go 14 in advance that's only 28 pages in right or 20 entries in rather so it's not going to give us the full range at all but if we change our limit to let's say 200 and an offset of 0 then it's going to give me everything right because i don't have over 200 entries so of course you can customize a lot of these things and that's where the documentation comes in really well so we did the limit offset pagination you can also do page number pagination i say pages it's similar both of these things are very similar pages just have like an actual page number uh where limit offset is sort of like a page number two but it allows you to give a amount that you're okay with for the lookup right so you can actually have a maximum limit as well to do that you actually have to configure you have to create your own limit offset pagination class and change it as as you'd like right you don't do it here you would just customize that uh which bit actually i think even might show you that and you can even change what's coming back through it as well also really really interesting this is where the django framework docs really really shine uh is like giving examples of things you're like oh i want to change my pagination a little bit how do i do that um so um overall though pagination is a really good idea when it comes to having more efficient lookups in your api now we're going to go ahead and implement a basic search so what i want to do here is i want to go into my products and i want to add in a another field in here that is just simply whether or not this is public so i'll go ahead and say public equals to models.boolean field and we'll just go ahead and say default being true realistically the default would probably be false but this is really to highlight what i'm trying to do with a search so we want to search only public records or records that we own so either or so now that i've created that new field let's go ahead and make our migrations as we do so python and manage.py make migrations and then python manage.py migrate now we're going to go ahead and create a method on this model so we can filter down a query set based off of whether or not it's public as well as whatever the query ends up being so to do this i'm going to go ahead and first off create a model manager so product manager and this is models.manager and what we're going to do is define a method called search where it's going to take in query and the user potentially so what this is going to do then is essentially saying something like product.objects.filter and we want to filter public being true then we also want to filter filter that down even more maybe the title and i contains being that query right of course we also probably want to filter this user but before i do that i actually want to implement this in a way that is well how django would do it which is by creating a custom query set so we do product query set and this is models.queryset and now we're going to define first off we'll just define is public and it takes in self and then we return self.filter public being true okay so i want to use this query set in here the first step is changing this to self dot get query set git query set is a method that's built into the model manager that actually refers to the query set methods that you can wrap in as well so what i need to do now is define git query set we are just overriding the default here so i'm going to go ahead and bring in args and keyword args and then we're going to go ahead and return this new product query set using self.model and or passing in self.model and then using self.underscoredb which is just using our default database this is actually a place where you could potentially use other databases as well which is kind of cool but now what i can do instead of this filter public being true i can now just use dot is public and that will actually run this method here and i can also then do a model manager or a query set a product query set a custom query set that is with the query as well as user be none and in here i can now go ahead and return the self dot the filter of that query and so now we would then do dot search query right there and then passing in that user main user okay so going down this a little bit further we can actually make this lookup more robust so i can actually do lookup equals to well let's go ahead and just first off make it really simple and just call it that okay so this is going to be my first lookup and i'm going to import something called q so from django.models or rather from django.dv.models we're going to import q and i'll put q right there so this allows me to write my lookups like this and then i can also do let's say for instance this is going to be our query set now and then if my user is not none then i can filter it down even further so qs.filter being user equals to user right so this is getting that first filtering this is going to filter this down further and then we can return back our query set but of course i can make this lookup more robust and that's actually what i want to do put a pipe here add that queue again and we can say content now i contains that query as well so what this lookup does now is it will look in both the title and content fields the data inside of there and it's going to look if there's a match so if there are matches that relate to this query that basically are contained in there it's going to look in there and then we're going to filter down even further based off of that search user and that's one of the ways that we can do a query search inside of django um so let's actually now implement this as its own view so what i want to do is actually create a search ma app so i'll go and do python manage.py start app and call it simply search now the reason that i actually create a whole another app mainly is so that i have views separated out from my other views i certainly don't need to do that but i think it's a nice and easy way to make things happen so what i want to do now then is inside of my search view i want to bring in well some sort of views so we'll do from rest framework we're going to import our generics here and i'll call this class we'll call this our search list view generics and list api view the query set is going to be products.objects.all by default and we need to import product of course that model so products.models import product we also going to import from products.serializers we're going to import the product serializer okay so serializer class is equal to that now what we want to do is well a couple things first off we want to verify again that what we're looking up in our search is public so what we've got here is our search saying is public in the manager method what i actually want though is that it's done in the query set method so i want to for sure have is public on whatever search i end up doing right so both of these things are repeating themselves it's not necessary any longer to repeat it here but if i run search i want to be able to make sure that the data that i'm searching is public because i'll show you why in a second and that has to do with the fact that we're using this query set by default so if i do get query set here self args and keyword args and i do query set equals to the super of git query set all this is doing is calling my default value of a query set which is this right here now if i do results equals to qs that search which i can do because i made this product query set this product manager and then the last thing of course i just need to do is objects equals to that product manager that means that this method right here can be run on any query set and it just verifies that it's public okay so that's kind of the key here so the search is going to be what our query is that query is going to be based off of request dot get and dot get q we'll look at that in a moment but i'll do q and then user equals two well by default the user is going to be none then i'll just say if self.request by user that is authenticated then i'll set the user equals to that request user and now i can narrow down these results or include those user results is actually what i want let's put the self dot in here so i actually have another problem with my querying right now is if the query itself does not find anything um you know perhaps i want to also look in this user model in other words perhaps i want to include the user query set not necessarily all results in here too right so perhaps i want to actually query it down by this and combine it with the results that are similar to that but only for the user right so it's possible that i have non-public data from my user as kind of the key there so the way we would do that then is we'd first off run self.filter so this is going to now be qs2 self.filter this and then dot filter that same lookup again now it doesn't matter if it's public or not so then my final query set is going to be the combination of qs and qs2 and then dot distinct so before if the user was not not none we just actually narrowed this query set down further this is not what i wanted i wanted to combine those two results and that's what we get here okay so now i've got these results here and i want to return those back and there we go let's go ahead and break this down a little bit and or actually let's go ahead and first off just bring in this as a url itself so i'm going to do urls.pi here and we're going to do from django.urls import path and url patterns equals to an empty list and it's simply path and empty string here and we'll do from.views or from dot import views and then we'll bring in our search list view so views dot search list view as view and then our name being search okay and there we go so then we just need to map this in to our main urls let's go ahead and grab this api here and do search and search urls great and so far so good and now let's go ahead and take a look at our search urls let's do a quick search here and it looks like maybe our server is not running correctly no module named search let's go into our settings and add it and then maybe restart the server i should definitely have a module named search so perhaps it was just something i just needed to restart and there we go looks like everything's working now and can i use none as a query value of course not okay so back into my views basically if queer if q is not none then we'll go ahead and do this otherwise we'll just say the results are product dot objects dot none so by default we are having the results v none and if the query set is coming through then it will actually search down from that query set so the final query set is assigned to results the initial query set is just simply qs so refresh in here and now i've got 0 in here so if i do a quick search with q in the url setting this equal to like hello hitting enter now i'm getting hello coming through you know seven times and what i actually want to look for here is i want to look for something that's related to the staff user so i'm going to go ahead and grab one of these just let's say pk3 so pk number three we're going to change the user real quick leave it in s public for a moment we'll save that we'll refresh in here hello it's still got a count of seven now pk three should have a different user and let's see where did it go pk4 there it is right there so here's our other user it is showing up coming back in here let's change that from being public hit save refresh now there's only six so the query has been limited which is pretty cool so of course the next step in this evolution would be then to try this out on the other user and search in there they get seven as a response which makes sense because this one right here is actually technically private pretty cool so the product serializer is probably not necessarily the class i would want for this one i'd probably want to actually have a search serializer that might declare whether or not something is public or not but now i can actually feel good that if i am doing this search as a user i can actually go in here and i know that this is public or private and of course updating my product serializer it kind of makes sense at this point to bring in whether or not it's public here and save that and this one's not public so now if i refresh in the search i see that it's not a public data but i am still being able to perform that search okay so this is how we do search functions now just to recap a big part of this is this query set here and really these q lookups but of course one of the biggest downsides of this search method at this point is merely the fact that it's only tied to the product model so there are ways to make it work a little bit better for us in terms of django we're actually going to take a look at a way of a third party service a another api service that we can actually wrap in to our api service so that our search is robust and can work across multiple models and it queries a little bit better than what we have here maybe not a little bit better a lot better than what we have here and so that's what we'll do in the next one and for a few videos to just really refine how our search works now on one hand not every product is going to need a search feature but on the other hand it's incredibly useful when you start to build rest apis that need to search across your data i actually think search is becoming more and more powerful as a third-party service but also the fact that we sort of expect it now right so even if you're building just an api like we are here especially if you have a lot of entries it sort of makes a lot of sense to have a search feature um so seeing this sort of basic example uh will work for a lot of different search cases but it's not necessarily going to be the most robust in the long run at this point our search is not great it works to some degree but it's not providing very relevant results now you might be wondering wait a minute your query is hello world and the first thing that's showing up does say hello world the second thing that's showing up also says hello world but of course the problem with this is actually the ordering of this data if you look at it the primary key of two is the first item the next one has a primary key of four the next one has a primary key of 22 and the pattern that we're seeing here is the oldest entry is showing up first now yes we could change the ordering of this but this gives us insight to the fact that this search that i implemented is really just a better filter it's just filtering down data it's not really providing that robust of a search and of course that is exactly what algolio will do for us that's why we're using this the other part of this is it's going to teach us how to integrate an external api service now in our case it's going to be a search service but it can be all kinds of api services and our end users whoever is actually using this rest framework doesn't necessarily have to know because of the response we'll end up giving here so there's a lot of really good reasons to learn how to use external apis so let's go ahead and take a look at the algolia one first and foremost go to goliad.com and create a new account it's free to get started and it's free everything that we're doing here will be free as well and it gives you quite a bit of things that you can do so the first thing is you're going to want to log into your dashboard you're going to see something like this now if it looks like you're building an application already that's okay too because we're going to do that right now as well so i'm going to go ahead and create a new application so you might log in and see this and so in my case i'm going to go ahead and call this application my django rest framework and i'll call it app i'll say it's free that's the only one i want but of course if i was doing pay as i go look at this for let's say for instance 25 000 requests a month it's only 15 now this is going to improve your search a lot especially over time which might make a much better service that you have now if you're not ready to spend 15 a month to improve your search well then perhaps you should just stick with the free plan for a while and see how well it improves your search because you get 10 000 free so anyways let's go ahead and add our data center next and what we want to choose here is the data center that's going to be closest to our production data center right so like if i was deploying my application in new york i would choose us east now i'm only doing this in local development and i'm in texas so i'm going to go ahead and use us central but of course you're going to want to change this when you go into production or you could just create a new application for your production app which is probably a good idea too anyway so we're going to stick with free and yes this is free i agree to these terms and all that let me go ahead and create this application now and this is where maybe you started here maybe not but the idea here is i'm gonna stick with experienced user because i'm gonna walk you through all this but by all means go through the new to algolia user setup if you'd like so after i do that boom i now have my django rest framework application now my case i have another one called a video membership this is for a fast api version of this which is quite a bit different actually because if we go back into algolia.com jump over to developers into documentation over to integration and what do you know django right there so there's a built-in integration for django this is the first step that we're gonna do we're really just gonna be setting this up and it's going to be tied directly to our models which is great this is exactly what i want right i want something that's tied directly to my models just like my serializers are as well so let's go ahead and actually implement this now this guide right here is actually pretty good i'm going to go further than what this guide has in the long run but by all means go ahead and play around with this guide here i think there's a lot of good information so the first thing i want to do is install the you know package the algolia search django package the officially supported package which is so cool anyway so at the very top of here i'm gonna go ahead and put in algolia search dash django i will use greater than 2.0 or let's say greater than or equal to 2.0 and then less than 3.0 this is directly from their documentation so i'll stick with that and we're going to run pip install requirements requirements.txt and looks like maybe i'm in the wrong place let me just back up a little bit here and run that again to make sure that i have algolia search installed great easy enough so far next up of course we're going to go ahead and jump into settings.pi i'm going to scroll to the very top here and right above these i'll say this is third-party packages and these are internal apps right right above that i'll go ahead and put in third party api services which you might have others right not just algolia search maybe you have an email one for example i'm going to go ahead and do algolia search underscore django just like that and i want to run my migrations but as soon as i go to do that it gives me this attribute error because i've been running this server and if of course if i try to run my migrations as well or simply just migrate i'll get that same attribute error this of course is because i don't have the configuration for this project so let's go ahead and scroll down and start putting it in so algolia equals to just an empty dictionary let's save that and run migrate again and yet again it gives me another error now this is just showing me insight to the configuration i need to use of course if you go to the documentation it shows you it right there and in fact it even gives us some things that we could copy so if we did copy them i'll paste those in there and there you go so this right here i'll show you exactly where to get these these values in just a moment but this right here i would usually put into environment variables so to do that i actually use something called django dash dot env and it's not python dash dot enb it's django dash.emb which reads from a emv file now the only reason i'm not implementing this in this project is it adds complexity to something that maybe is already complex enough and it adds some time so by all means if you're curious about this let me know i have a lot of different things that cover it but anyways so let's go back into algolia there is one more thing i want to add in here which is going to be my index prefix and in this case i'll go ahead and add in cfe by all means add whatever you like but i'm going to add that in as my index prefix which we'll see a lot in just a little bit so before we go any further let's go and see where these keys are coming from exactly in this documentation to me there's a great sign of documentation that has the keys in there already filled in for you let's go and take a look into our dashboard here we've got the django rest framework app if i click on api keys and i scroll down to the application id here's that application id that we have and what do you know it's the exact same one the next key you might think oh maybe it's the search only api key but it's not it says this is the public api key for your frontend code the admin key is the secret one you definitely want to keep it secret and it's only for your backend so if we copy that one or look at it and paste in here what do you know it's the exact same thing pretty cool all right so now that i've got all of the configuration done or the baseline configuration done for this package i could run migrate okay so now i need to actually create my first index now the index itself is stored representations of our values so it's actually going to take this data that we have in our database and put it into their service so yeah we absolutely can accidentally expose data that we shouldn't be exposing of data for that reason i am very particular about how i design my index that goes into algolia so if i go into products here i'm going to go ahead and create index dot pi and i'm going to go ahead and just start off with class product index we'll fill fill this thing out in a moment but i just want to just highlight what it is that i'm going to put in here by declaring the fields i want available here so let's say for instance i had a password field i don't want that password field in this index let's say for instance i had a customer you know email field definitely not most likely there's certainly times that you might actually want it in there but i'm not going to do that right for this i really just want to be very specific about the fields that i'm going to use in my case i'm going to go ahead and use title content and let's say price we'll also throw in that user there as well let's just you know just reiterate there's the user title content price and then heck we probably want to have that public value as well so these are the default values that i'm going to use for my product index which again just means that algolia is going to essentially take a copy of everything i have in my database that corresponds to these fields and they're going to be mapped pretty pretty clearly okay so how do we actually implement this well first off we're going to go ahead and import the class that needs to inherit from which is algolia search underscore django import algolia index and we'll bring this in here next we need to use our actual product model so i'll do from.models import product now maybe at some point it will be model equals to this but at this time it's not like that that is a little bit closer to the serializer or the actual you know model forms and stuff like that but it's not like that instead we use a decorator that goes from algolia search underscore django.decorators we're going to go and import register now this decorator here i'm going to use it on my index and push in product but the way i actually think about this is very similar to the django admin when we do admin.site.register and we do our product and then our product model admin this is literally how i think about these two things and how they correspond to one another the product model admin doesn't always have to declare the model itself is really the point of that but there's our index okay so now of course the biggest question now that i've created all of the conditions to have this index how do i let algolia know about this index well the first thing i want to do here is run python manage.py and take a look just python manage.pi to see all of the management commands i have in here there's a lot of them one of them being algolia reindex clear index and apply settings pretty cool so if i actually grab the re-index one which is the one i want this will actually do something pretty interesting the following models were re-indexed product if i run it again it works so now going into the dashboard that i have for this application and let's jump back into it and there we go so here's our search here i'm going to jump into search and let's go ahead and close out this product list here and what we've got here is a number of records is 30. if i search hello world my old friend and hit enter look at all that it's doing a search and if i scroll down a bit i've got two entries in here that actually show that and i can see all of the attributes to this which in this case it's showing me attributes that well relate to the fields that i chose it also gives us object id this is definitely the primary key this is the one that's stored in our database all of that was designed by default for the package but these fields were ones that i picked now what if i said i don't want non-public fields to show up how could i do that right so let's go ahead and go into our model i'm going to define a method here an instance method that's going to say is public and it's going to take itself and it's just going to return self.public okay so it's just checking some arbitrary you know condition in here but really it needs to return back you know true or false right needs to return back some sort of boolean value otherwise this is not working correctly so once it returns that boolean value then we can go back into our index and say something like should index and then pick whatever that field is or whatever the method is the actual function itself now the reason i like having the function versus the actual field is because then if i had let's say for instance i had a publish timestamp field in here as models.date time field and all that then i could reference and say if now is greater than or not now but now it's greater than self.published timestamp then i'll go ahead and return you know true or false or something like that right i'm not going to implement this right here but that's the point of having one of these functions gives us a little bit more robust control over whether or not it should be indexed inside of index.pi or being displayed as public in other places but now that we've got this i made a change to how my algolia should be indexing all of my data so i'm going to go ahead and run this again and at this time well it actually had one less now depending on where you're at maybe you had no changes here so what i want to see is does it change in real time so back into my admin here let's make sure our server's running so python manage.py run server and let's refresh in here i got this public value here hello my dear old friend i'll change that from being public we'll save it we'll go back into the search console itself and i can just refresh on this search because it's in the url itself and when i do refresh it notice that one's gone and it's the instance of 35. if i change it back hit save and refresh again oops not there but rather our index if i do a refresh in here now it's back and what do you know it's object id of 35 object id of 35. so it's actually updating my index based off of that should index value that is amazing that is exactly what we want when we're building something like this we want it to have the ability to really quickly change what is being indexed and if it should result in our actual searches now if you think about this in our current method toggling between what is public is tricky right certainly this method right here could change over time and then maybe you need to run reindex again so back into the back end you know running our python managed up high you know if we do that and come back up to algolia reindex you might have to do this every once in a while but thinking back to this search method here how would i actually change it if i'm like oh i don't want this is public thing based off of this i would have to you know do a number of things related to the instance method itself and then maybe changing the actual query set method as well in other words it's cumbersome just to toggle whether or not the search results will show this right or whether or not the index will actually have that data now the other thing is we don't have to have this index being shown up like in other words i can keep this public value this true false value on there and i can narrow down i can actually filter still further down on this right this is really just really meant to state is this going into algolia's console or not and of course there's other items that we can do to augment this index we're going to talk about a number of them but for now what i want to do is one more thing and that's related to tagging so let's say for instance i had another model called tags so let's say tags model values and those values are going to be something like electronics and cars and boats and movies and cameras whatever right so we've got a bunch of tags in here that you know if i was a little bit more further along on my models perhaps i would have it as a foreign key in here but i'm just going to keep it in as a simple list here and i'm going to go ahead and import random and i'm going to go ahead and declare a new method in here and called git tags list and this is going to return back a list itself so what i'm really just going to do is random.choice of one of these tags and again still keeping it in as a list so i just want one of these per index value okay so now that i've got that the next thing i want to do is go back into index.pi and go ahead and add in tags like what is the method that i can call to declare tags inside of algolia and it's literally tags like it's like these kind of values comma separated tags just like this we're going to go ahead and call git tags list so i changed my product index so what do i need to do i need to re-index everything so it's going to re-index in some cases depending on how much stuff you have stored this can take a really really long time and so now that i've got it re-indexed if i do this search again or refresh in the console of algolia i should be able to now filter things by these tags so if i go in the query filter and do say cars for example let's go and search this in this case it actually did give me a result so i've got a tag filter of cars and the actual query of hello world my old friend come down in here i see that tags of cars is working let's go ahead and just go to hello with cars being tagged now i've got a few more well i only have two in this case right um and what if i did electronics so and let's change that tag and add that parameter of electronics and hit apply and electronics i've got another one so this is yet another thing that just gives us a little bit more robust of a query right and going back into our search here yes there is a way to filter things down based off of what i just did but it is not going to be easy it's certainly not going to be nearly as easy as what we'll end up doing with our own algolia stuff as hopefully indicated by how their console works and how simple their console is as well so we in the next one we will actually implement a client to do and perform searches on algolia's api but as of now we are really have the foundation of building out a lot of different indexes or indices um so the other thing about this is let's say for instance you wanted to add in other kind of indexes so you can also have our product quick view index or our product um electronics index or some you know something along those lines where we can have other indices specifically for the model itself now i actually don't think it's always that practical to have multiple indices for any given model there's probably some debate to that it's going to be based off of the fields of course that you may want to in include but the other thing is you could also create an index for your users or basically for any other model that you have which is really nice so that means that wherever you are you can do really robust searches so let's say for instance for blog posts if you want an index just for your blog posts there's two things that you could do you could index the blog posts and you could search across the blog posts and the product index it's pretty great so now let's actually implement our search client for algolia now that our product model is in the algolia index we're going to go ahead and create a client an actual search client that our django project can use so inside a search here i'm going to go ahead and create client.pi and we're going to import from algolia and search underscore django import the algolia engine okay and so and this is backwards there we go so we're going to define git client here and this is going to return back the algolia engine dot client now the reason for this is so that we can do something like client and get client and then say index equals to client.init index and then whatever our index name is which i'll just tell you right now it's cfe underscore product we'll come back to that in a moment um but the reason for this to even create a client in the first place is if you go into the python api client documentation or really just the documentation for various search methods in our case we're doing the search index you'll just see this index.search and you'll also see something like this which this is basically taking oh well there's already a client initialized that's what this is okay and so to me i actually then go ahead and say get index and say index name and what my default name will be is mine is cfe product and then i do this same sort of thing and then return back that index so we can pass in this right here and we'll return back that index so i can use it in other cases and also different indices as well okay so now what i want to do here is i want to develop a method called perform and search okay it's going to take in a query and then we'll also pass in keyword arguments as we'll see so the first thing is i need to grab that index which is simply just get index right and then i want to perform a search it's really easy we do results equals to index dot search and then whatever our query is and then we return those results wow shockingly easy isn't it okay so let's go ahead and implement this on our views and this time i'm going to call this old search list view to something different maybe search list old view and now what i'm going to do is say class search list view and it's generics.generic api view let's bring that back dot generic there we go and then we're going to go ahead and define our get method which takes in self request args and keyword args now certainly this could be not a get method but that's what we're going to leave it in as then i'm going to go ahead and do from dot import client and then just call our results being cli client dot perform search and whatever our query ends up being so that query is going to be request dot get dot get of q just like what we have down here but now i actually have the request on here and there's our results okay so with these results we have to return something now you may have forgotten what we need to return but if we go back into the product model itself like the view here or our other one product makes it actually doesn't help us but the actual function view does because it has this response class so that's what we're going to return i'll grab this response class and we'll come back into our search here and we're going to turn the response of the results okay so let's just say if not query then our response will be something different let's just put it in as you know an empty string and status being let's say 400 something like that okay cool so back into our django project we've got a search engine here and it's already working cool so hello world searched and it's giving me back all sorts of results in here result highlight check that out so i can actually iterate through all of these results and have the highlight show up and it emphasizes with html it emphasizes the two matching queries that's pretty cool it gives us fully highlighted matching words what if i just said hello same thing gives us the highlighted values which of course is not really that easy to do when it comes to how all of this actually worked in django itself so this is cool i now have this ability to do this right and all of the items are public and of course the actual object ids you know look at it and be like okay we've got 4 and then 35 and then let's see 27 so the ids are varied quite a bit so if i search something like my old friend and hit enter you know it's gonna it's gonna change exactly how these results end up being so what i also want to do though is i want to be able to narrow down these results i don't want them just to be a general query although even with just the general query it's already a more relevant search than what we had and we get a lot a lot more context in here for each field that we have right now i didn't search anything by tags so let's go ahead and see how we might do that let's go ahead and do the tag portion of all this so what i want to do then is come back into my client and i want to do something about these keyword arguments here so initially speaking i'll go and say tags is an empty string then i'll say if tags is in my keyword arguments then i'll just go and say tags equals to keyword args.pop of tags or it's an empty list now okay so we can have tags being a string or a list in this case with this search method now what i also want to do is add in a params dictionary a default one that's empty let's just verify that empty default one doesn't do anything to our search it doesn't break anything just yet and it didn't so with this i can now see that oh well i have tags here and realistically now i'll just say is if the length of those tags is not equal to zero then i'll go ahead and update my parameters so this is params and tag filters is equal to those tags or whatever was popped from here again if i do a quick search got nothing now if i do an ampersand here and tag equals to let's say cars and hit enter of course the django view didn't change much other than the fact that this right here now has two different git parameters query and tag or q and tag now in our search we can actually add that one in and say tag equals to request.get rather get dot get of tag and i'll just leave it as one item here you can make this more robust if you want but now i can do tags is equal to whatever that tag is right or i could actually just use the tag itself in this case i'll also go ahead and say or none let's refresh in here and now it's saying cars notice that there's one result here that's coming through okay and if we come down here we see our query says hello my old friend it shows my also my parameters that are coming through as well let's go ahead and change this to electronics to see if it actually does change and what do you know it does so already again our search is just that much more robust okay so one of the things we did when we were creating this this actual search feature is we had this staff user in here right so the staff user we still want to have but the thing is i want to allow the staff user to see in results that aren't public anymore right so i'm going to change my index a little bit to narrow this down also a little bit so let's go ahead and go back into our product model into index i'm going to go ahead and get rid of this should index method here based off of that boolean value of public i'm going to leave that one in here but now what i'm going to do is i'm going to allow for me to narrow down based off of whether or not it's public and also maybe our user so what i want to do here is declare settings and we're going to go ahead and do attributes for fauceting and this is going to be our user and whether or not it's public we also in this same breath we can also say searchable attributes and declare which fields are actually searchable so like title and content you know perhaps we don't want the user field to be searchable this does that so it really just narrows these things down quite a bit okay so let's go ahead and actually bring this filter down a little bit more okay so this is going to take a little bit of pythoning so let's go ahead and actually open up the python shell here and we'll do python manage.py shell and what i want to do is essentially rebuild this search engine index okay so i'm going to go ahead and do from the search client we're going to import all and the reason for that is so that i can manually put this search feature in here so the idea now is we want to grab the index filters so essentially anything else that i'm going to pass in here other than tags let's say for instance i did let me just put these in here i'm going to do perform search and hello is our query tags are going to be just electronics let's put that in as double quotes as well or just empty and then maybe public being true this is how i want the search feature to end up working so that means that all of my other keyword arguments i'm going to actually put in as index filters and this is going to iterate through each one so we're going to go ahead and say that the key value is a string just like this and then we're going to do 4k v in keyword args.items so this might be an empty list of course so if not or rather if it's not equal to zero the length of it rather it's not equal to zero which it certainly can be then we're going to go ahead and add on these parameters not as tag filters but now face or rather faucet filters and then we'll go ahead and add them in like that so again it's another list list of items and it's just going to filter things down there are more advanced ways to filter this like this and that and many other things but we're going to keep it nice and simple or as simple as it can be for now and again these are the attributes for fauceting so i can only really filter by these two items which you know maybe in our client we should probably check that the key value pairs are only in that list but i'm gonna just keep it fairly uh you know loosey-goosey here probably not great but anyway so let's go ahead and grab this search now paste this in here now i did change my index from that public so i also need to update my index so python and manage.pi algolia and re-index okay so this should give me my private values as well okay so now i'm going to go ahead and perform that search and it's going to be hello and then we'll go ahead and say that public is false we hit enter and what do you know it does a search for me does public is false and there we go what if i use a keyword argument that doesn't work well then it passes that right so it actually tries that keyword argument that doesn't work which is why i made it sort of flexible like that and of course if public is false and let's go ahead and do tags are equal to what tag did we have for the one that actually worked the tag that we had was boats okay so let's do votes public is false gives me the same one and if we do public is true and it gives me a different one right so we've got boats here and now true so now it's quite a bit better of a search it i know this is probably still confusing overall but but the idea here is we now have the ability to search by tags and filter down by any given argument that we might have so the other aspect of this is if i just then said user equals to staff then i can actually bring it down even further right so now i've got a completely different search so let's go ahead and update our view now and we'll go ahead and say user is well none if request.user.is authenticated user equals to request.user and i'll go ahead and just use their username here so now of course we can say user equals to user and then if there is another filter that we want to have like public is true or false we could do that so i'm going to go ahead and say public and get dot get public and i'll just give this string event being let's say equals to z or is not equal to zero basically so that'd be false so the default is true right so that's what that would end up doing unless it was zero okay and so now we've got a little bit more robustness one thing i need to fix in my client is really just say if v just to make sure that there is a value if there's not a value we don't want it to show up let's go ahead and see if that ends up working and say public user is none okay so yeah it doesn't look like it ended up putting that one in cool so now let's go ahead and take a look at this query i got to make sure my server's running so python managed py run server come back in and now i search hello and what i've got is nothing so it's for the staff user right and so what i want to check here on my search let's just check all these different queries so i'll print out user query public and tag let's save that refresh and we've got public as true okay uh i got tag being none which is fine hello being none as well great so now that i've got that let's go ahead and change and public being hit enter and what do you know it's now toggling between that great so and it's also going based off of that particular user right so it is only showing this user stuff and that would be true if we had no query as well which by default we need a query so let's just put a in there or e in there that's funny it doesn't even do it for a single letter but anyway so we do hello actually i think this user doesn't have very many data so let's go and put a in there there we go so the other user actually does give me some results quite a few results actually and all of these results are public by default based off of this cool so that is our algolia search client now there are more robust ways to do this search as i mentioned but i think this is actually a pretty solid way that you can do where you're controlling the entire view you're basically controlling the entire aspect except for what's actually being returned that's really the only piece that algolia is doing in this case it's taking in our data it's ingesting it and then it's returning back what we're searching for but we have a lot of granular control over all sorts of things the other cool thing is you can absolutely filter by price like if something's over or less than something so the documentation i think is really good for all of these things and the reason i wanted to show you how to build this stuff was because the documentation does show you just generally speaking like how to add in items like that like if you look for a search or search faucet values you can see here are some faucet values you look at the examples and here's some of those those examples right so we have some in there as well and so this this is essentially what i just did but instead of being filters it's it's faucet values which there is definitely documentation all over the place on how to actually find exactly the way i did it um but the idea being that we did that key value pair and that's exactly what these parameters end up being so if we print this out again and take a look at that search for any user really and say and public being to one or zero and tag being let's say cameras get another search in there and now we see exactly what the data that's coming through for those parameters is right so we've got our tag filters and our faucet filters um pretty cool i think overall this is like one of the one of the one of the best things that we could do at this point without getting too far advanced if i change the user let's see if that actually ends up changing it won't change it for me but of course if i change public being one then we get that one in there too so of course the only reason that the user is not changing is because of how we set it on our view we didn't set it as a parameter so now the big question is do you make this get request or a post request that is a question i'll let you sort of think about what type of request it should be and really the way you can learn that is by looking at what actual search engines do like google search engine or amazon search engine or in this case of course algolia search engine in the console how does it actually make requests and should you follow along with what they're doing of course my answer is yes now the other thing is something we haven't set up yet but we will is a front-end client something to actually consume all of our api as well as our search and we'll take a look at how to use another feature that algolia has in conjunction with our current application to have better searches as well on the front end to make it a little bit easier for our end users now when it comes to creating rest apis what you're going to want to start to think about is this unified design to how you serialize and index your data what i mean by that is if you take any given model you want the final serialized data and the final indices to match what other models might look like right so if we've got our product model here for example i have user title content price in public that's fine the model can be in that representation but if we go into our serializer this is when we actually have clients that we want to consume this now we're talking about something completely different potentially right so i've got content in here so what i mean by this is let's say for instance somebody on your team or you know someone who's showing you how to do this sends you a brand new app that you didn't actually build and they send you a model that looks like this no content in there but rather body right so if you're sharing these things we got content as our main body content for the product whereas article body is really the content of that article so two different fields here and they're represented in two different ways right so this one says body and then our product says content and it also has a lot of other stuff in here as well that our article one does not i really just want to narrow in on just this content versus body section to really highlight why it is that we need to have a unified design okay so if we go into the api first let's go into products here and what we see is if i zoom into content i've got content no big deal right if i wanted to actually edit this thing i would go into update and this would be my editing portion notice that it says content still it does not say anything related to body now of course if i go into my articles though and let's go into articles what i can see here is its body content so we already have a mismatch as to what is the primary source for the body but again we're going to be changing what we've done as in our product serializer to match the new stuff as in the article serializer or let's closer match it right it's not going to have everything the exact same but it's going to at least illustrate what we need to do in this case so going back into our products let's jump into the serializer now and we want to change this to being body now there's a lot of different ways on how we can think about approaching this the first one might be just to go on the product model itself create a new property similar to like that is public method here new property with the property decorator and define body and it takes in itself and it just is going to return self.content right so what's cool about this is it will just come in and it will actually respond back with whatever stored in the database so then if i jump into my serializer i can go into that content and just change it to body no harm no foul i refresh in my list view here and hey it looks like maybe it's not refreshing let's go ahead and oh yeah the decorator that's silly and the decorator is not callable so we'll leave it in like that and we refresh in here so now we've got body in here and that it says now i have content now of course if i go to update this like one update what i've got is well nothing the content field is gone right this is not what i want at all i want to be able to update this content still in my api so what to do well of course it goes back to the serializer itself inside of the serializer what we want to have in here is simply body and we're now going to go ahead and do serializers.characterfield this time all i need to do is add a source this source is going to be content so now that i've got that i now have a serializer and i can actually make changes to that content using the field name of body right it just maps directly to content which is great so if i come in here and say another new product and this is my new content let's go and make sure it's public i'll change the price to something outrageous and then we'll go ahead and say put and there we go right so here's that new content it's all in there it's looking good we can verify this if we go into the admin for that single product there product id of one what do you know everything changes although the field in the actual database is still content but how it's represented is now body so now we've got title and body and also public and that how's that going to correspond to let's say for instance api and articles okay so api articles doesn't have the public distinction here maybe we don't actually need it i'll explain that in a moment but we do have title and body so at the very end of the day what we've got is our end users will or our actual clients the ones that are going to consume this api now have a better unified cohesion across the two different kinds of data types or the different stored data that's product and article types right so two different kinds showing up pretty cool now let's actually go into the search portion of this if i search for anything or a new content or something like that what do we call it we said it was another new product let's go and do a quick search for that so slash search and another new product there okay so here's my result here notice that it still says content this is probably not that surprising because i didn't change much in terms of how it's indexed so let's go back into the index for products and now of course instead of content it's just going to be body and body okay so now that we've got that we need to run our re-indexing command so python manage.py and re-al golia underscore re-index hit enter and we got zero articles we'll come back to that but we've got 30 products if i refresh on this search content goes away body comes in fantastic okay so along with some sort of unified design and cohesion one of the things we also might want to have on here is maybe a path right so if you think about our products when the web version of this is showing up we might want this path right here where maybe with our articles we're going to want to have this path right of course i'm not actually going to put these in action on this project but the idea is we want to have those paths and so they're basically going to be the model name plus the primary key or whatever the object id ends up being okay so that's what we want to do now first off we're going to go into our product and we're going to go ahead and define path this time i'm going to leave it in as a property you can have it as an instance method as well but we'll leave it in as a property so path and this is going to return again the lowercase of the model name basically and so we'll go ahead and just call it product plural and then self.id or let's be more specific with primary key okay so now that we've got that i'm also going to bring this in over to my articles model and we'll paste this in here and of course this is going to be articles cool so now i have some cohesion with those two things again going into my serializers for product i'll go ahead and put the path at the very bottom this is basically a read-only field here because well it's a property of the instance itself and then we're also going to do that same thing on our articles okay so we refresh let's go ahead and jump into products and there's our path maybe i want to have a leading slash that's probably be a good idea for both of these things there we go arctic products and articles great so now that i've got this path let's go ahead and add it into our index as well path save product index just like that and in articles as well save it and we're going to now re-index again and sure enough we got products reindexed let's do a quick search again this time just hello and the result here now has the path in there so again this actually helps us quite a bit when it comes to working with any given search now when it comes to the index as well what we have we have the path but we also might want to have the url right and so typically speaking what you'll see is git absolute url in here i actually really prefer url i shouldn't say typically but a lot of times you'll see git absolute url because by default what you'll end up seeing a lot of times inside of django projects is the get absolute url method and in this case i'm going to go ahead and return the api version of this and so it would be better if i actually had reverse coming through in here but i'm just going to hard code these things for now just to illustrate the purpose of them so again we've got two methods in here get absolute url on both of them and then yet again i'll just go ahead and define a property and i'm going to define it as url and this one is going to return self.getabsoluteurl right so the reason that we have the url method in here is because if we are having this as a client we've got where they can actually look up and maybe this should be endpoint instead of url so it's kind of the thing that you want to think about here is not so much what you actually name it but whatever you end up naming it you want it to be consistent across all of your projects right and so this git absolute url is interesting because well i actually want this to be my entire url i don't want it just to be that and that's something that we'll have to do another time and i would also leave it with you but anyway so we've got urls on the attribute there oh yeah we need to make sure that we are changing when we do change those methods the property methods that we update the index as well for each one okay and these also might you know maybe we put them in our serializer fields as well but the serializer field does have the url so again having things cohesive it probably makes sense to call them both url and have them somewhat hyperlinked identity fields but at this point it also makes sense to be like okay well in my product serializer you know perhaps i don't want to use this anymore or edit to really just match what i've got with my articles and now i just have in point and everywhere else is just simply endpoint and that hopefully would have our absolute url going through let's go ahead and do that re-indexing again and it looks like we did it okay so we do a search and now it's got my endpoint and actually to me this actually makes a lot of sense to say path versus endpoint endpoint being the api endpoint path being what you want the display path to end up looking like if you're using like react or view or any other sort of front-end javascript library here so yeah i think i think we covered a number of things related to this but one of the things i wanted to get into was why isn't it that the index is not actually indexing articles why isn't algolia indexing articles when on the display here the article list it actually does show articles well um so there's a couple reasons for this and one of them being if we go into our models here what we've got is this is public method so the goal of this is to see if the item is public right so or if it's in the past so what it should be doing is is public should also match the public manager here right so make public being true and publish data is less than or equal to now in the past now should be greater than or equal to and we'll go ahead and refresh that here and now it actually does index these things just a really slight little change if you make a mistake and that's where this shouldn't index is coming from just like what we had before but this time it's just based off of a boolean value and a published date so the cool thing about that of course is now if i wanted to pull an article i could go into any of them and just take off the make public say no and now if i try to reindex everything i should only see one article in there because it's no longer public and it actually would have already been updated in the algolia console so the idea of having unified design across your api is not something that's just like trivially solved solved here it's something that you'll have to do consistently across your apis now i also would argue that you would end up doing this consistent action in your models as well like it should probably actually start in the models than in the serializers or the index values right so models up high this is actually where we should probably correspond to a lot of things but it's not always possible and it's not always something that you'll necessarily remember so that's actually kind of the key thing here that i wanted to get across as well that you'll start with the models but if you don't have control of the models you can't change the model for whatever reason then your index the way you index these things as the well as well as the way you serialize these things that's going to be where you can create that unified design across many many different models and applications inside of django now the last thing to really kind of tease here is the idea that our search client at this point only searches one index so if we want to have multiple indices which we sort of do we might need to update and change how this is i want to leave that with you for now and see and challenge you to figure out how to actually make that happen how to use different indices and i will say the other one that we do have in here will be cfe article because it's going to be cfe underscore which is that you know pre-pin value that we had and then article is the model name so that's the other one definitely want to challenge you to do that look it up on your own and try and get that performed search happening and a big part of the reason to do that is it's going to leave you with another question if we have a unified design of the index values we're going to start to see this look identical across the board maybe we do have this price tag on here but maybe we don't and if it is starting to look identical is there another field that we would want to add in here to distinguish between a product and an article and whatever other models and of course the answer to that is absolutely i'll give you a hint you'll probably call it object and it'll probably be directly related to the name of the class that you're using cool so now that we've got this let's go ahead and switch gears a little bit and start working on jot authentication tokens in a moment we're going to implement the json web token authentication for our django rest framework but before we do that go to this blog post right here so that you can scroll to the bottom and download the api client that it created for you now this client you're going to put into a file called jwt.pi inside of your pi client just like this you shouldn't have to change very much to this jot client we will look at it a little bit but overall the way it's set up is very similar to what we've already done with a few added features by all means read a little bit more inside of that client itself i try to give some notes as to how it ends up working so a json web token or jwt is actually pronounced jot i don't know why please somebody tell me but the idea behind it is we aren't actually storing this token into our database this is a little bit different than the auth token itself instead what we're going to do is we're going to have three endpoints that will actually allow us to create a token or obtain it refresh that token as well as verify that token so that's actually one of those things that we'll need to do so now what i want to do is actually start installing this thing so let's go ahead and go into requirements.txt and underneath the django rest framework i'm just going to use january's framework dash simple jwt now it also is important to note that the django rest frameworks recommended jot package has changed over time right now it's simple jwt it didn't used to be this so whenever in doubt just go to the django rest framework documentation of course and look for their json web token authentication that they recommend this definitely has changed so it could change in the future but the concepts behind jot aren't really going to change very much as you'll see so now that we've got that there let's go ahead and install this so pip install r requirements.txt and of course i already have it installed because i was doing some tests here and so now what i'll do is i will go into settings up high and we'll add this one in simply rest framework and then underscore simple jwt we'll save that and then i will also add into my default authentication classes for the django rest framework uh the simple jot ones and it's simply this right here now i got this directly from their documentation it's also in our guide i think perhaps not but it's definitely in their documentation as you'll see in here so if you go into installation we've did this we just added this now we need to add their actual token and whatnot so the actual paths for this so this is another thing that we'll put in to the api module so in urls.pi and again i'm going to go ahead and copy and paste this stuff because it's pretty straightforward on how it works let me know if you would prefer me to type it out but anyways so now we've got this and three different views so i think they're actually pretty explicit as to what they do the first one is to obtain the view to actually obtain the token the access token as well as the refresh token the token refresh view is how you actually refresh those and then verify will verify your actual access token the key for our client our jot client here is to make sure that these endpoints are slash api and token in this case they certainly are but you definitely want to make sure that that's the case otherwise you'll have some issues with jot.pie okay so now that we've got all of these things implemented i'm going to go ahead and run migrate so python manage.py migrate just make sure there's no migrations that need to happen which there shouldn't be anyways i'm going to go ahead and run this server now and now i'll run python pi client slash jot up hi now just like we've seen before it's going to ask us to log in so our username and our password and it looks like maybe i did invalid token so i got an invalid token here this is actually the error that i wanted to see and also the other cool thing is i now also have creds.json in here so let's actually take a look at this first so we've got an access token and a refresh token this access token is actually what we're using for our header so going back into jwt we see that we've got a header type of bearer so when we actually use the headers we've got authorization the type it is whatever that is and the token is the access token right so this is not really not any different than what we were doing with our other authentication where we actually grabbed the request the response all that stuff and then we just grabbed that token and put it in here and this is actually where the problem is this is why we're having an issue with our access is because both tokens both authentication methods are using the same type of authorization they're both using bearer right so that's why it's saying invalid token here so how we solve this is well it's mini fold number one we could change our jot token header so we can come in here and do simple jot put in some configuration on settings up high and we can say off header types and we can change this to instead of bearer like it is that's the default we could change it to like let's say jot right i actually don't want to do that this is a symptom of a problem i created on purpose so we'll leave it as bearer and actually while we're here i'm also going to add in access token lifetime and we'll talk about that in a second and then i will copy this and change it to refresh token lifetime now the idea here is i want to have these things expire pretty quickly while we're testing so i'll go ahead and navigate to the very top and import date time and then we'll go ahead and add these in as daytime instances so the lifetime for the access token will do daytime.time delta and a seconds being 30 maybe even shorter than that but let's leave it in s30 and then the refresh token lifetime will do minutes being one now typically speaking you would probably have this in as maybe let's say like hours being three and then this being like a day or days being one uh maybe hours even being one you could even consider it being five minutes or something like that this is something you'll have to play around with but the general idea is when you have a shorter access token that just means that it needs to refresh more often assuming that you can refresh it once you can no longer refresh it in this case after a minute then both of these tokens fail and they'll need to re-authenticate which we'll actually see cool now the other thing i want to actually look at is inside of credits.json the actual access token itself i'm gonna go ahead and copy this you can just watch me for now and i'm going to go to jot.io and go into the pasted token here and what i see here is a bunch of information about this jot token it gives me a user id this is actually important because it gives me the user id of the user that is actually authenticated right so it's giving this authenticated users data so we actually want to make sure that this is not exposed right so if you used a custom user model for example you're not going to want to have the actual user email out here you'd probably just want to have the id and even that might be something that's a little bit too exposed so that it is one of the downsides of using jot but it is something important to know that you can parse but it is something that's important to know that you can actually parse out the jot token into a few other things related to the data that's in it but it actually is important to know that you can actually parse out a jot token and the things that are related to it now in some cases you can insecurely add a bunch of well private data to this jot token that's not something i recommend instead if you need data about this user do a request to the back end for that because we have the expiration and whatnot all set up okay so how do we actually solve the problem though of this authentication if i've run this again it's still saying invalid token now and actually again going back to the header types it has to do with that but before i even change the header types what if i change the ordering of these tokens right so let's say for instance we have session first jot second token third now let's go ahead and give it a shot this time it refreshed the token and it showed me that there's two different lookups that were successful essentially so it did a couple things it refreshed the token for us so it gave me new credentials um and then also it does actually do the lookup so the ordering of these default authentication classes actually does matter so if you have something higher up it's going to go off of that first now of course if i wanted this other order for some reason and did the authentication again it's still going to say invalid token so the actual symptom of this is because of our token authentication class i purposely wanted to break it so that we could see that this right here probably we should just leave it in as the default which is token and so now if i run it i shouldn't have any issues so the other part about this is if i wanted to quote unquote simulate logging sewn out with the python client i would just delete creds.json and then now with my new client it's going to ask me to log in again and we would log in again and there we go so now we've got access granted and we're able to actually do these lookups right and so this time credits.json is definitely back but this time the token itself should be different than what we saw before like if i paste this in here it's actually a different token even though it gives us some of the same user data and so that's actually part of the client itself being able to do this refresh and it's actually really simple on the refreshing itself first off we've already seen the perform authentication all this does is write the response to a file called credits.json which i named above but the idea here is there's that data and it writes it in next we have a verify token here this doesn't actually print anything but it will verify this token on some of the requests to make sure that it is a valid token that's what this is doing right i think that's a good step to have when it comes to using your actual tokens we have a way to clear them out and then we have this perform refresh all this does is take in our token header that original access token and then passes in that refresh token and then what do you know it actually gets us new data in here based off of the response from our refresh token now the actual refresh token itself doesn't change but the access token does and then we rewrite to that so it's a lot of stuff that we could definitely go through but the reason i just want to skip over it because you can hopefully at this point read through it and now you have jot authentication in your project which is something that we're going to use a little bit more now the thing that i really do like about this is the fact that we can really just expire out those tokens by default so after 30 seconds or a minute they no longer have access so after both of these times elapse they're gone they're done but especially after the refresh token has elapsed notice that it does this i talked for over a minute refreshing token failed invalid data you need to log in again right so all of the things related to it have failed and we can see this with this request right here so we've got verify that failed so let's go ahead and try and refresh that also failed so we log in again and now it's working and i do this again notice that it is verifying this token on the request now you don't necessarily have to verify it on every single request that's not always necessary but it is a good idea to do if for some reason it's tampered with at all um to just verify hey this is a valid token still let's go and try and refresh it if refreshing fails then we'll go ahead and just have to issue a brand new token and so that's pretty common to do that's one of the reasons that shot has become so incredibly used is because it's not stored in the database so there's no way of tampering there and then it's also has these access and refresh ability over time that you can really modify on how you see fit which also is pretty cool now we're going to log in with a javascript client to our django rest framework now what this is going to highlight is this allowed hosts key here and the challenges in sharing resources across different hosts we'll talk about that a lot in detail in a little bit now the other thing here is to show you a practical implementation that's not based off of a python client but rather a web client of some kind and that's why we're creating a javascript one so inside of our main root folder here i'm going to create a new folder called js underscore client and here it's going to be two files index.html and then we're going to go ahead and create the client.js okay now naturally these two could be in the same place but i'm not going to do that i'm going to have them separate first off we need to create our html document here and it's just like this i'm not doing anything any really complex when it comes to html or javascript to be honest so the next thing is we're going to go ahead and declare a form and it's going to have three inputs in here the first one is text you'll have a name of username and a placeholder just saying something like you know your username and then we'll close that off and then we'll do input type of password and the name being password and spelling that correctly and the placeholder being your password the final one is going to be a submit button so input type being equal to submit and this of course is to submit the form and the value is just going to be simply log in okay so as it stands right now there's nothing really uniquely identifying this form other than the fact that it is a form element on this single html page but of course if i had other forms in here none of these are uniquely identified so what we do is we can use an id here this is incredibly common as you may already know this idea i'll just call it login-form no big deal this is going to be how javascript can access the data that's entered into this form next we're going to go ahead and do script source equals to dot slash client.js and close that off this of course is referencing client.js right here now to run this we just navigate into there so cd into js client and run python dash m http dot server and then a port i'm going to use 8 111 and now i've got that port going which i can open it up here but i also i actually want to use just simply local host so i'm going to write localhost here you could also do 127001 but i'll stick with localhost okay so what is it that i'm trying to do with this form well first of all i want the post method here and of course if i actually try to run this let me just refresh this page real fast let me try and run this and go ahead and log in i get a 501 right so the reason that this is happening is because by default this form can't go anywhere it's not being sent anywhere so javascript's going to come in and prevent it from being sent to whatever the default is and then we'll redirect it to to where we want it to go so the first thing i need to do on client.js is we need to grab that login form so when it comes to javascript for variables if the variable's not going to change we use const as in constant we could also use var which means that it can change or let which also means that it can't change we'll go ahead and just stick with const as a constant here we're going to go ahead and do the selector called document.getelement by id what do you know it says element by id this is a form element and what do you know it has an id so i can actually pass in what that id is in this case it's just login form that's it it definitely is there but of course if we want to make sure that it is there we can also have a condition saying hey if this login form's there you know handle this login form now what do i mean by handle it i mean that if it's submitted hey what do you know submit there's a type called submit if it's submitted let's actually do something with it this is just a event handler so we add something called a event listener to that specific event and that's submit then we create some sort of function to handle this so the function will be called handle login and it takes in an event by default that's what is going to be passed to it from here now notice that we write out function here instead of define but overall it's roughly the same and then we use these curly brackets just like we did with this if statement here instead of spaces that's how javascript works if you weren't aware so we are going to go ahead and handle this login so at this point i don't have a whole lot going on so what i want to do is prevent the default action that happens with this event we can also console log this event out save that and again my server is still running on this javascript client but i'll refresh it because i made some changes so let's go ahead and log in here i'm going to go ahead and inspect the element and actually navigate over to the console here now i'm on chrome so you could always just go to view developer view source or more specifically we want to go into the javascript console itself that javascript console is available in a lot of browsers in here we can actually write out javascript so everything that i'm writing i should be able to test in here as well just like that think of the javascript console like the python shell if you weren't already aware of that anyway so let's go ahead and log in here i've run that and i see that there's this submit event here right so it's giving me the event that happened and the error did not happen because i ran this event.preventdefault so this just prevents the form from being submitted like that you can also have event listeners on buttons and all sorts of cool things if you want to know more about javascript let me know even if this is confusing for you just a little bit stay with me because i want to show you the error that comes about so what i want to do now is i actually want to submit this data somewhere where do i want it to submit well of course i'm going to go ahead and say my base endpoint up here is going to be http colon slash localhost colon 8000 slash api now 8000 is deliberate 8011 or 811 is also deliberate those are meant to be different on purpose they are not on the same host as far as django is concerned okay so now i've got my base endpoint here so what i want to do is i want to send it to my let's say for instance my login endpoint that's going to take in we'll use these backtick backticks here this is how you do string substitution in javascript it's just dollar sign curly brackets with these back ticks which are usually above the tab so shift and or actually no shift just backtick that's it okay so here i've got my api i'll just do slash in this case i want token i want the jot token here okay so that's going to be our endpoint then we're going to go ahead and set up our options here so i'll go ahead and say const options equals to we want our method being well what http method do we want in this case it's post we want our headers and actually the we don't have to put quotes on the key value pairs in javascript headers is going to be a dictionary value content type and it's going to be application json then we need to add in some body data here so what data do i need to add in for now i'll just have it as an empty string next we're going to go ahead and do fetch and fetch is the actual method so we do fetch and then we pass in these options okay so thinking through what this is like in python going back into let's say for instance our list view here the first part here is that fetch that we're essentially doing we're basically doing just this right now in this client it's just different a little bit so fetch in this case is kind of running like requests dot post right it's very very similar the only difference is instead of having keyword arguments for all the data we just pass in it in as a object which is this dictionary value here that's a javascript object okay so we'll figure out the body data in a moment but let's go ahead and try this out so i made some changes to this client let me restart the server in here and we'll go back into it and i'll try and do login okay so my console's in here if i scroll down a bit i now get has been blocked by course policy so i attempted to do a request to local host 8000 api token from this origin now we will solve this with something called django course header in the next section but before we do i actually want to get this data ready to be sent because right now i sent nothing to that endpoint so there's actually literally nothing being posted here so to turn this into something what we can do is we can take this form this element right here i can actually extract all of that data by saying well we'll go ahead and say let login form data equaling to new form data of that login form so this class right here is built into javascript at least on the browser that you can actually get the form data from a form element this of course is a form element git element by id so that's the element itself and what this does then is allow me to then turn this into a object so login object data equals to object.from entries of this login form data and so now what i can do is console log that data okay and you know if you're familiar with javascript a little bit you might be wondering why i'm using this method specifically versus you know attaching ids to the inputs themselves the nice thing about this method here is it doesn't actually matter what the inputs are on the form itself this will turn all those inputs into some login or some actual object data that's what these two lines do it's pretty cool i really really like this method in any case let's go ahead and try this again so now it should actually give me the data that i'm passing through here right and there it is there's that object itself and so i can actually think of this object as something i can use with this data so if i use the key method here i can say username i could just log in that username here or console.log it and just run something like that and what it'll give me is the the username that actually came through cool so that then means that i finally can wrap this into my request by making json.stringify this data so by default it doesn't come in javascript object notation although this is technically a javascript object the notation part is missing this turns it into an actual string itself so what i can do is say let body string equal to that data so we can just see the difference between these two so if i do console log of the login object data and that body string now i can let me just refresh just to make sure all the changes happened and we'll just put in some data here it doesn't really matter what now i have two different data types right so this first one is an actual object that has key value pairs in here this second one is just a string of data that's actually a json string something my api will know how to handle my api knows how to handle a couple of different data types here but this is typically what you'll end up doing with your json data is you'll turn it into a json string which is then attached into your body itself and then we send it somewhere but of course now we actually have to handle this fetch method here but before i go down to the back end so let's actually do what's going to happen with this fetch method so what we can do is do dot then and we get the response back then we'll use an arrow function here this arrow function will return response dot json we can also take a look at what comes into that response itself then we have another promise that we're handling that's that's essentially what this is doing is handling a promise this will handle that json data that comes through here so we can do another arrow method or arrow function here and console log x now these two things will only work once we implement the cores headers with django but we should be able to see the response at this point okay so let's go ahead and refresh our page and i'm going to go ahead and unpreserve this log so it's all clear now i'll preserve it again and i'll hit login and now it's showing me this this data here and it's actually still not even getting to the response itself so i'm going to go ahead and do catch this will catch any errors so i'll do console console.log error this is actually probably where i'll see the actual error itself so we save that and let's try it again and there we go so now i actually can see the error coming through uh in this method okay so to me if you're not familiar with promises and and you know all the things related to javascript asynchronous handling of things this is it right here okay so this is definitely maybe a complicated part if you're really just a python developer and wanting to see this stuff the general idea is this right here returns something back called a promise this right here handles that promise so that dot then is like a brand new function that goes on this promise and then handles this data and then if these two things aren't here it can still catch that data again it's complicated if if you aren't that familiar so i don't want to spend a lot of time on this but do let me know if you want to learn more about javascript at this point we're going to go ahead and handle a this you know cores issue that's coming up because it's incredibly common especially as you're learning because of this no access control allow origin header on the requested resource itself it's a security thing that we definitely need to address now we're going to implement django cores headers now this package is maintained by my friend adam johnson he's a fantastic contributor and has a lot of really good django books check it out if you like this package and the work that he does anyways so what we want to do here is we want to implement django cores headers so that our javascript can well overcome this issue right here and of course this is a security risk potentially right so if you don't implement this correctly then yeah you might just have everybody accessing your api all the time perhaps that's what you want but it's also perhaps what you don't want so we definitely want to be careful about how we implement this so i want to go ahead and go through some of these things right here it's pretty straightforward on the documentation itself but let's go ahead and make sure that we have it all installed first off in requirements.txt we need to add in django course headers in my case i already have it in there i believe i put it in earlier so let's go ahead and do a pip install r requirements.txt and yeah it's already installed yours might be as well so next thing is going into settings.pi we'll scroll down to installed apps and we'll just add in our third-party packages here cores headers save that whenever i add in a third-party package into installed apps i go in and run python manage.py migrate i already know that course headers doesn't have anything that should be migrated but i do it as a default action okay so now that i've got that inside of my middleware above common middleware i want to go ahead and add in course headers.middleware dot cores middle where okay so me personally if i know that i'm going to be using the django rest framework and some sort of web client i often implement this course middleware right off the bat with very minimal settings the minimal settings i'll do is cores urls rejects or regular expression this regular expression is just the api endpoint that i actually want to use so slash api dot star okay so now i've got this somewhat set up let's actually see if it works like this now my case i still have my backend running and my javascript client running you know perhaps you have to restart some servers and stuff like that let's go ahead and give a shot in my localhost i will try and log in and i still get a invalid error now i noticed something off of the video this content type is just like this right we need a dash in here this is what course headers is looking for so if we scroll down in the documentation you can actually see the allowed headers so this is another cool thing that we can do if we absolutely wanted to limit the headers that we wanted like content type we could also implement new headers as well so if you created your own token for example and you wanted to use different headers you totally can with that and so content type is just content dash type in the case of course headers you could always add both of them in i think the django rest framework knows both values um but cores is just a slightly different anyway so so i changed the content type let's go ahead and try this again and log in i still get that blocked by course headers now this is where the actual domain makes a big difference or the url that i'm you know requesting from this is called an origin so right now i'm requesting to this origin so localhost 8000 from localhost 811. now if it was on the exact same origin as in the same port as well we would totally be able to request the data we wouldn't have any issues in fact we probably wouldn't even have a course problem in the first place but we're not requesting from the exact same location we're requesting from a different one so let's go ahead and do cores allowed origins and now what i'll do is add in all of the allowed origins that i want in this case http colon slash localhost colon 811 and of course if i had https coming through here same thing and if i wanted coding for entrepreneurs.com to have it with the subdomain and the subdomains are important coding for entrepreneurs.com would be right there right and you could go down this line for every allowed host that you want to allow i don't think you want to have any particular domain i don't see why coding for entrepreneurs would be necessarily doing this request anyway but the idea here is you want to have the correct origins for interacting with your back end okay so now i'm going to get rid of this preserve log here refresh on my client and try and log in here and what do you know it logs me in and it gives me my tokens pretty fantastic all right so that's it for our course headers for this series now the idea here is you might need to add additional configuration to match your specific needs now of course when you go into production you probably want to turn local host off right so if we said if debug then we do course allow origins plus equals to whatever local testing that you're doing you know that's probably okay and that's probably actually the preferred method because you don't want to just all of a sudden have localhost have access to it but no other site does and so debug in this case would be probably the preferred method of adding in your allowed origins or of course using a different module for production and development for settings but in any case we've got now better setup for local testing with all kinds of web-based clients now so with whatever web-based client you're working on go ahead and do that now the other thing in with these allowed origins you know perhaps you want to have a lot of your allowed hosts in here as well but unfortunately you need to include the protocol too so you can't just like reference allowed hosts being your allowed origins as well it has to be the entire origin at least the root of it it doesn't need the paths this is where the pads are handled is the core's url rejects and of course we could change that too we can have more specific urls that are allowed so there is a lot of different configurations so i do definitely recommend that you check out the django course headers package because there's all sorts of good stuff in here and we'll probably see more things come in over time as well as course continues to evolve since it's a security idea and a security issue this will definitely evolve over time although what we just did here is probably pretty fundamental and will be for some time to come so that's it for this one now what we need to do is come back into our javascript client and start to handle some other things related to it so we can actually interact with more of our backend as we've seen with the python stuff now we're going to go ahead and use jot with our javascript client so inside of our handle login here we actually want to store this data the actual data that's coming through on a successful response assuming that it's even there so the idea here is i'm going to create a new function and we'll call this handle off data it takes in just the off data for now and what we want to do with this off data is use local storage to set item so setting item is a key value pair so key and value so the first key that we want to set is really just our access key which is in offdata.access and not typecachebut.access so the same concept for refresh both items here okay so this function now instead of having it console.log i can just pass in that function right there that will actually handle it for us then so if we take a look at this and actually log in we can see notice that i have the application open the application tab with local storage open as well so let's go ahead and do our login and i get my access key and refresh okay so right off the bat this should show us potential security risks with storing this data right here now actually talking about all of the security risks with this are well well beyond this course the general rule of thumb here is if we are going to store these keys i highly encourage you to do it over https that is https on here as well as with your rest framework right so that means in production you're not using insecure websites that's one thing and there is a way to lock down the jnrs framework to only accept https requests we've already seen it that's these allowed origins so we don't have to allow http in here we could just deny it and you know what then they won't be able to have access to the tokens at all the other thing is constantly having them being refreshed is probably a good idea but overall if it's on the browser in local storage there's a chance that the security could cause issues again this keeps changing a lot with jot tokens themselves so http only storage is sometimes used so http only but we're not going to go into all those methods instead i just want to see how to implement this and make it work and we'll worry about security better security i should say in the long run i think we already have a good amount of security in here it's just better security is something that we just can't spend a whole lot of time on right now anyways so now we've got this handle off data we have those tokens so it's finally time to do a proper lookup so what i want to do now is let's say git product list and this shouldn't actually be that big of a surprise of how we're going to do this first we're going to go ahead and grab the endpoint so i've got my login endpoints now i'll just call it endpoint and we'll go ahead and say products then we'll go ahead and set our options so our options being you know what kind of request this is method being get that is the default request headers we are absolutely going to have to set our headers in here so the first one is going to be simply content and type and this of course is going to be application json and that's all we're doing right now then we're going to go ahead and do fetch to the endpoint with all those options then we'll go ahead and you know grab the response and return back response.json then we'll go ahead and grab that data so i'll go ahead and just call it data and what i want to do with this data is i want to store it or actually actually display it on index.html to do this i'll just go ahead and create a div called id and content container and that's it okay so grab this content container and then up here i'll go ahead and say const content container equals to document that get element by id of that content in container and then i'll also create a new function and we'll call it write to content and i'll just call this you know data and all we're doing here is say if the content container exists then we'll do contentcontainer.interhtml equals to we'll do a pre-formatted string that string is going to be json.stringify of this data like we've seen before and we'll go ahead and do slash pre so all this is doing is just creating new html with the data i pass into it on this container element so this write to content or let's actually call this write to container i'm going to go ahead and now grab this data and say write your container of that data okay so we can also console log that data if for some reason that write to container function just doesn't work for us okay so let's go ahead and take a look i'm gonna refresh in here we're gonna log in and i have a problem already so first off the access token stuff went away when i did the incorrect values for login which is actually pretty great we do want it to be cleared out right so we didn't actually get proper off data which is fantastic and of course the next thing is i actually don't have the product list being called anywhere so on one hand i could call it right here and just have it attempt to do it when it first logs in or what i could do is come back up into this handle auth data here and now just go back into calling this off data and passing in that function itself closing that off and passing in off data here but i can also pass in a callback as in when the off data portion is finished what could we do we can pass in callback and then say if callback then we just call that so it's really just calling a function that i put in there so basically after these tokens are set then call the get list function which is this now i don't need that i mean perhaps i want it but for now i don't need it i'm going to go ahead and now log in and author you know my credentials were not provided so that often the authentication there the reason they were not provided of course is these headers right here so i can go ahead and write them in authorization and bearer let's just write in an incorrect token let's take a look at what that does okay so let's log in and now it gives me this if i want this formatted just slightly better what i can do on the string if i say null and then four and that will give me a better look at this there we go so now we see another kind of token happening okay so let's let's just actually manually call it every single time for a moment and so there is saying that i've got an invalid token if i don't have a token at all it's just saying that the credentials were not provided which is clear now the credentials are provided and they're incorrect which is also clear hopefully so the final thing here is just using the actual store credentials to do that we're going to use this string substitution method here so dollar sign curly brackets like that with these ticks on the front and back so we use local storage again git item this time and then the actual key that we stored just a little bit ago you probably want to make these keys a little bit more unique then access and refresh but that's fine for now okay so now let's go ahead and log in notice that i'm still undefined here i'll log in run it and sure enough there's my data if i refresh in here that data is still coming through because those tokens still work which is fantastic but after a short amount of time those tokens should no longer work right they should actually expire it's going to be about 30 seconds before this will expire at least it should expire based off of my current settings and so then the lookups may or may not actually come back for us so that's pretty interesting the other thing here is this options method here much like write to container i probably want to change it into a function and we'll go ahead and say cut get fetch options and this is merely so i have some defaults right and so the idea here is then returning back this data so really just return what that data is and again const options equals to get fetch options but of course as you know the method is not always going to be git so we can put this method here and basically if the method is null then i can use git otherwise i can use the method okay so the r the method argument that is of course and then we also might want to have the body data in here whatever that is so again if the body exists this time i'll go ahead and put that body in otherwise we'll use null and the body data will be here now you could also make it as like the you know js object and then if it is a js object then we would do json.stringify something along those lines much like what we did up here but realistically i'm just going to leave it in his body to make things simple on these options so it's just body body and butt if there's a body we'll do that otherwise we will not so now my get fetch options can work in other places now i can actually think about that same idea with fetch itself it doesn't necessarily have to be get fed up fetch options but rather just have some sort of callback for the successful data but i'll let you play around with that and that's where learning a little bit more javascript could really help you here but the other thing is me personally i would use something more like react.js in this scenario um versus writing pure javascript like we've been doing anyway so now when i refresh in my access token here i get this token not valid so this is a correct response right so if we go back into our console we are seeing what the response is this is like not an error that's happening okay so this is the response here so realistically what we want to do is let's put these curly brackets in here once you put those curly brackets you need to return response.json now we can actually go into console.log this response right here refresh now i can see the response right and so when i get a 403 perhaps that's when i need to take it one step further and actually re-log in you know so i can actually handle that kind of response right here or i could actually handle it up here so we'll do function and um is let's say token not valid so this is going to be my let's say response data or json data we'll call we'll just say json data and we want to look for the code message in here so if jsondata.code and jsondata.code equals to that exact code token not valid then maybe please login again or you know run a refresh token pattern or refresh token query or fetch you know so this part i'll leave up to you but now when i run this let's go ahead and make sure that we come in here of that data so this is really just validating it refresh and now again it's please log in again it's not i mean it's showing that original data but of course we don't have to so the other thing here is i can just return false and or return true and then we'll go ahead and say const valid data and then if validata we can write to that container finally and naturally this is a simple way to handle token not valid errors but if you remember back to our urls our actual api endpoint we have a way to verify these tokens so in our javascript client what we could potentially do is when we resume on a browser of any kind i could actually come in and run validate jot token something just like this now obviously i just copied and pasted some data here but everything that i have in here is not really outside the scope of stuff that we've been doing and just the options have changed a little bit and so now what i could do is actually run that validate method right off the bat instead of anything else so when they come back in here then i get this token is not valid here and from that then i would probably just clear out my local storage or something along those lines to have the user actually log in right so basically just checking that code again right so it's similar to what we just saw so this is not token data let's actually see if that ends up working for us and so now it's saying please log in again and there we go so this is actually also a opportunity to run refresh right so refresh token you know and if that fails if there is a refresh that fails then then you can remove the local storage and run that other error right and so that would be one of the ways to do it one of the many ways and of course still you know checking your data to make sure the token's not invalid um but the goal here is not really to build a front-end full-on it's really just to sort of think through how to actually consume this rest api as it currently works and part of that is actually running refresh and verify with these tokens because that's something you'll have to do from time to time so at this point i actually want to challenge you to figure out how to do that refresh based off of what we just did for validate as well as the handle login method itself i think it's pretty straightforward the documentation's in there and you know maybe at this point your javascript skills are there if you're not interested in learning more about javascript don't take that challenge down you could always reference the python version as well because that's not a whole lot different okay so now what i want to do is take this one step further and actually implement a search feature that's based off of our search api that may or may not need our actual tokens now we're going to go and implement our search api it's going to be very similar to our login form but now it's just going to be our search form the actual input itself will be q for query this time the placeholder should be your search right whatever that is and then we'll get rid of the password portion here post data doesn't really matter get data whatever the method here doesn't actually matter because we're going to handle it with javascript but it should be a get method the value itself will be simply search okay so let's save that and we'll take a look at it there's our search there and so what i want to do is i'm going to perform a search in the client or handle it with the client that is so let's copy the login form and call this a search form and search form very similar to the login form so we'll go ahead and copy that and say if search form add event listener for submit this time we'll call it handle search shocking i know next we're going to go ahead and copy the handle login method here now what i may or may not need is my authorization but we'll deal with that in just a little bit and we're going to change handle login on the second one to handle search base endpoint is simply search i'll just call this endpoint now the question is the actual form data itself so let me just cut these out and this is going to be we'll just call this form data and just simply data and of course this is coming from instead of the login form but the search form so now i've got this data and this endpoint i do not want to turn it into body data let me go ahead and get rid of that this is actually a get method and so what i want to do is actually add url parameters to my endpoint and it's actually pretty straightforward on how it's done there's a built-in way to do this by actually creating something called a url search params object so go and say let search params equal to new url search params and we just grab the object itself the data from that form and so with that i just come in here put a question mark this is how you designate search parameters into a url and then we put dollar sign curly brackets those search for amps it's really just that simple next of course we're going to do our endpoint here and this time it's not off data but rather just simply data and so i'm going to go ahead and write to the container like we did here so write to container of that data okay so i'll talk about the authorization in a moment but now we'll go ahead and handle that search and i'll say hello world quick search and what do you know here's the data and of course that data comes back as a object itself so if i do data dot hits i can console log that so we can do console log you know data.hits and again searching hello world exclamation mark there it actually logs out each one of those objects so that means that i could of course iterate through those and just display it a lot better which is just not something we're going to do at this point obviously because this is not about building the best front-end with javascript as i mentioned okay so now we have a way to see that data what about our actual authorization right so what's definitely going to happen here is if i put in authorization and bearer and then you know some random token let's say that and do another search here of hello world hit enter i get this token invalid error of course right so it's not a valid token at all so the authorization here this is where actually having my validate jot token method that would make a lot of sense to validate it right off the bat and also handle set validation right handle it in some way whether it's refreshing or whatever so what i want to do here is let's go ahead and look at our application and we've got our tokens in here they actually are in there and so now what we want to do is grab one of those tokens but only if it exists so i need to change how my headers are written only slightly so the first thing is i'll go ahead and say const headers is equal to first is just simply the content type okay next i'll go ahead and say my off token equals to local storage dot get item and we've been calling it just simply access or nothing basically if auth token then we'll go ahead and go into our headers and set the authorization header right so we can now set it in here and we can use string substitution for that auth token and now of course we can set our headers just like that so this is for those views that may or may not need that token so if i get rid of these tokens in here i can search hello world and have no worries if i log in and log in correctly that is and do a search i can now see what that search is again now of course if the token itself is changed and do a search again i get an invalid token so naturally i would want to handle this in some sort of fashion not like what i just did but handle it where there's actually some javascript that shows us and to give you a little sample of what we could do to improve the user interface for this we could actually just set something along the lines like this again we're validating whether or not the token data is there we're also checking if the content container is there if it is there we're going to empty it out then we'll check if the data is there if the data is there we'll do stuff with it otherwise we'll just say no results found so let's go ahead and try this out we'll do a quick search for hello world and please log in again okay so in this case our actual access tokens are incorrect if i get rid of those this time it does show up which is maybe not the behavior we want perhaps we always want them to be logged in and again if i do this login here we go if i do something that has no results let's do let's say like i don't know if this will there we've got no results here this time it actually did have hits so there actually was hits in here so really maybe we'd have something along the lines of checking if the hits length is of certain length so or perhaps we put in here let's just go ahead and say if the hits the length is you know equals to zero then we will also put in this data here okay so we do some weird search and i gotta log in again again weird search no results found hello world bunch of results found great and so this is okay right again i would use react.js or something like this to handle this and make it look a lot better and have a much better user interface but the overall idea of all of this was really just give you a sample of how to use a javascript client with a rest api now the thing about this search method itself it has well a few things that are left to be desired one of them is like auto suggesting some of the searches as you type and so algolia actually has a feature for that for javascript so this is part of the thing that we've been working towards is seeing just improving our searches overall with our back end and it's still going to be basically the same data right it'll still result in the same data but it's just going to be a little bit more powerful so that's why we'll take a look in the next part now we're going to go ahead and implement algolia's instant search so let's go ahead and dive right in because once you actually start using it you're going to be hooked so going into algolia.com docs go to building search ui here and click on what is instant search so if you scroll down a bit notice that there's support for react react hooks view many other places as well as just pure javascript we're going to be using the pure javascript one but i am stoked that there's react if you want to see that covered in some of my react tutorials let me know otherwise let's go ahead and jump into the installation you also might want to check out the live demo on your own but let's go into the installation itself because i want to get right into this and we're going to be putting this directly into our page so let's click on that to get the javascript itself so i'm going to go ahead and copy this jump over to index.html right above my client.js i'll bring this in here and while i'm in here i'm actually going to go ahead and say form style and display none i no longer want my search form in here at all and so now that i've got the javascript side of things i want to also add in the styles as well so all of the css related to making this look even better so of course you don't have to use their styles you totally can style it yourself but i do recommend using their styles if you aren't great at css or if you just want to save a ton of time and still get something that looks great okay so now that we've got this set up all of our html is well almost done next what we're going to do is well basically we're going to declare our search box this is going to be the container element for it much like we did with the content container this is going to be just the search next is going to be the hits okay so these are the things we'll initialize a search field and then the actual results themselves so going into client.js i'm going to navigate to the very bottom here and we want to initialize this so the first thing is our search client this is going to be algolia search and then it's going to be our actual app id and our key now the thing about this is if we are in this installation and i scroll up a bit notice that these things are already filled out for me maybe not everything but some of it is so if i paste in here i get some code that's already ready to go including the search box and hits now where are these keys coming from here so if we go back into our you know our application itself and we go into our api keys first go into the overview here still in the django rest framework app go into api keys application id there it is right there there it is right there and then the search only api key is right here you might want to regenerate that from time to time and in fact when you go into production you'll probably want to have these keys actually retrieved by some api endpoint that's not something i'll do at this time at this point i think you can do it yourself now that's also true probably for this instant search here the actual index itself like what index do we want to use in this case it's going to be cfe product now generally speaking i like to avoid hard coding any values that could change over time inside of my javascript or of course hard coding api keys so all three of these things would probably be better served as an api endpoint that we just use but to spare some time i'm just going to omit those for now so that's it that's all i changed just a few things some copy and paste so let's go back into our project and what do you know i already have some results in here and if i search hello world my old friend i actually get results and it's happening in real time it's searching everything but of course this is not how i want this to look i want it to look well better so how do we go about doing that it has everything to do with this hits portion we can come in here and say templates and we can go ahead and say item and then set in an item related to all this and so and the first thing is i'm just going to go ahead and do title just like that that's it refreshing here there's all my titles this should look a lot like django templating engine or even jinja templating engine they're not identical but they're very close at least for rendering out things like this so i can also do you know a p tag and do something like price save that refresh and what do you know there's my price now if i put a dollar sign in here this is now causing some issues because that's a tick there right so we need to escape that with a slash and then now i've got my dollar sign so i can actually run inline create view and well only inline create view shows up i can do some gibberish and no results fantastic like look how easy that is i don't know i can't gush about it enough the next thing is maybe narrowing down our actual results right off the gates this is called a refinement list there's a number of refinements that we can actually use but i'm going to use a very basic one now the question would be is where do i find these values as well as the refinements themselves and this comes back to how we indexed everything so going back into our products and index.pi here's all the fields we can use literally all of those names and here is all of the refinements i can use so the attributes for faceting right and so our fauceting and so what we want to do here is narrow down based off of user for example so going back into the client we can go ahead and say instant search dot widgets and then dot refinement list and declare the container and i'll call this hashtag user-list that's of course going to be something we need to add so go into index.html above the hits here i'll go and do id equals to user.list okay so back into our client client here the next thing i want to do in this refinement list is declare an attribute in this case it's going to be our user okay so that attribute again is something else we can put in here as a p tag and we can say what user it is just to you know see that actual user come out now in your mind replace user with authors or like brand or all sorts of things but what we see here is i can now narrow my results right off the bat based off of either one of these things right so staff we've got product new let's go ahead and just do product new and notice that cfe also has one and now i have both of these things in here now the other thing about this is we can use roughly the same thing i'll just go ahead and copy this instead of a refinement list you can actually just say clear refinements this is just a button to clear user selections or something like that or really refinements this is how their documentation has it no attribute necessary just make sure that it is in there and then we're going to go ahead and add this in now if you ever forget something like if you added like i just did and didn't actually add the element into the um and this should be clear refined mints excuse me not refinement we get container must be a stringer element right so it's not an element at this point so it does break it right unlike what we did with these if statements there now that doesn't mean we can't do it where it doesn't break in other words like you know this this doesn't exist on our html we could totally make it a little bit different to where it does some validation and some checks for it but i'm going to skip that for now and just say div id equals to clear refinements save that we refresh in here and now i can clear these if i have any and again we can have multiple let's see what it looks like with multiple i'm going to copy this and this is going to be called public list public okay and so back into index here user list and public list save that refresh and now we've got both right so naturally i could put another div here and just say you know h3 public items public okay um and so then we put that in there just like that and that will like separate those two things out a little bit uh but now we have a pretty robust engine for searching and it it didn't take very long i mean granted i mean i'm not doing the work algolia is doing the work but we had to have our index actually working even to get to this point but as far as instant search is concerned this is fantastic and we have a lot of customization over it now let's actually customize our result a little bit more right so this is okay but perhaps i want to have well some highlighting in here so let me just tab these out a little bit more so i'm going to go ahead and highlight the title for example and i also have a body element in here so both things maybe i want to highlight a little bit some of them have body some of them don't okay so i'm gonna go ahead and do a div here and a div here just like that okay and there we go so how we actually highlight this is something i'll just copy and paste to show you how it's done so we change it from this to this let me open this up a little bit and so first you just put helpers on the outside and then you gotta close it out notice the slash and hashtags and then you declare which attribute in my case i use title if i do the same thing with the body i would just need to change the attribute to body and then if i come back into my search do a quick refresh and do hello world now it actually even highlights my results and then my old again what do you know look at that i think that is so cool um anyways so that's how goalie is instantsearch.js i think this is a much more practical thing to use in all of your projects whether it's something that you're familiar with or not that is the javascript portion of it and the reason that we even built the javascript client was to lead up into this because a lot of you may have very little experience with javascript but even if you do have experience with javascript you see hopefully see how much easier it is to use all of this now some of you might be like well why is it styled this way why isn't it spaced out a little bit better and all that that's just because of how i styled it i didn't do much in the way of styling i didn't like put a sidebar and main content area and stuff like that things that i'll leave up for you so if you do have questions on algolia algolia instant search please let me know in the comments otherwise let's keep going hey thanks so much for watching hopefully you got a lot out of this one at this point i recommend that you learn how to build some sort of client to consume our newly created rest api when you build these clients as we did a little bit you actually see some of the back and forth the things you need to consider with what the data is in the rest api and then what your client might end up needing sometimes they're aligned really well from the get-go sometimes they are not that's one aspect of this the other aspect is to actually review other api services that are out there and see how they design their apis and see if you could do things similar to really just make your jnures framework api that much better and also let me know what you do i would love to see the projects that you're working on whether that's just a simple github link or an actual full on website let me know in the comments otherwise thanks again for watching and i look forward to seeing you next time [Music]
Info
Channel: CodingEntrepreneurs
Views: 388,129
Rating: undefined out of 5
Keywords: install django with pip, virtualenv, web application development, pip, django, beginners tutorial, install python, python3.8, python django, web frameworks, windows python, mac python, install python mac, virtual environments, beginner python, python tutorial, djangocfe2021, digitalocean, production, python, django3.2, web apps, modern software development, trydjango2021, django-rest-framework, REST APIs
Id: c708Nf0cHrs
Channel Id: undefined
Length: 421min 40sec (25300 seconds)
Published: Fri Feb 25 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.