Try Django 2.2 // Learn the #1 Python Web Framework | Justin Mitchel | Skillshare

Try Django 2.2 // Learn the #1 Python Web Framework

Justin Mitchel, Coder, Teacher, Entrepreneur

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
55 Lessons (4h 18m)
    • 1. 1 Welcome

    • 2. 2 Overview

    • 3. 3 Setup Django and Virtualenv

    • 4. 4 What Django Does

    • 5. 5 Define a View

    • 6. 6 A First URL Mapping

    • 7. 7 Multiple Views

    • 8. 8 path vs re path vs url

    • 9. 9 Your First Template

    • 10. 10 Loading a HTML Template

    • 11. 11 Add Bootstrap

    • 12. 12 Render Context in Templates

    • 13. 13 Stay DRY with Templates

    • 14. 14 Rendering Any Kind of Template

    • 15. 15 Template Context Processors

    • 16. 16 Template Tags

    • 17. 17 Your First App

    • 18. 18 Save to the Database

    • 19. 19 Model to Django Admin

    • 20. 20 Model in a View

    • 21. 21 Dynamic URL based Lookups

    • 22. 22 Handling Dynamic URL Errors

    • 23. 23 Get Object or 404

    • 24. 24 A New Database Lookup Value

    • 25. 25 QuerySet Lookups

    • 26. 26 A Unique Slug

    • 27. 27 CRUD and Views

    • 28. 28 CRUD View Outline

    • 29. 29 Blog Post List View

    • 30. 30 Routing the Views

    • 31. 31 Include URLs

    • 32. 32 In App Templates

    • 33. 33 Submit Raw HTML Form

    • 34. 34 A Django Form

    • 35. 35 Saving Data from a Django Form

    • 36. 36 Model Form

    • 37. 37 Validate Data on Fields

    • 38. 38 Login Required

    • 39. 39 Associate Blog Post to a User with Foreign Keys

    • 40. 40 Logged In User & Forms

    • 41. 41 Update View with Model Form

    • 42. 42 Better Validation on Update Views

    • 43. 43 Delete and Confirm

    • 44. 44 Blog Post Navigation

    • 45. 45 Include the Navbar

    • 46. 46 Include with Arguments

    • 47. 47 An Included Template for Consistent Design

    • 48. 48 Publish Date, Timestamp & Updated

    • 49. 49 Model Managers and Custom QuerySets

    • 50. 50 Published and Draft Posts

    • 51. 51 Static Files and Uploading Files

    • 52. 52 Image Field and Uploading Images

    • 53. 53 Putting it All Together

    • 54. 54 Complex Lookups

    • 55. 55 Thank you


About This Class

Try Django 2.2 is step-by-step to build a modern, fully open-source, Blog web application using Python, Django, Bootstrap, Javascript, and more. 

Django is an amazing web application framework that makes building web apps incredibly easy and powerful. Django is used by Instagram, Pinterest, and many other leading websites around the world.

Now it's your turn.

Also, everything we code in this course is on our github repo here.


1. 1 Welcome: One of the most amazing things about software is you can learn how to build something that can reach people around the world. Whether it's only five people, all the way up, toe billions of people, it's is pretty incredible that that's possible in this day and age. Now, in order for us to get to that scale, to actually reach those people and help those people, we need to start somewhere. And the way that you start is by building a Web application. And you might be wondering, what is a Web application? Well, it's simple. It's a smart website, so that means that it remembers things. It has a database. It has some security. It has sessions you can log in. You know, it has all of those features that you might be very familiar with now, right? So if you use something like Instagram or Twitter, you're already familiar with this process. Those are built on top of Web applications, so what we want to do is we want to take the basic concept of a Web application and actually implement it in something Riel, and we're gonna be doing that using Django. Django is a Web application framework written in Python Python is a programming language that I would argue, is very approachable for beginners. And I do recommend that you have some experience with Python prior to jumping in here. But Django, it is all in Python, and it takes ah lot of those concepts that build up Web applications. And it makes it really, really easy to build one with not that much time. And also not that much of a in depth understanding of everything that's going on. That is what attracted me to join you in the first place, right? I found it actually pretty simple toe build a basic Web obligation. And then from there I started asking these questions like, How do I actually change what I store in the database? Right? Like, That's simple, but it's a good question. And once you start uncovering these questions, you will start to want to dive deeper into Django itself and at also building weather vacations and all that. And I want to be here for that journey for you. So let me know if you have any questions in this series, we're gonna be taking you through Django step by step to make sure that you know how to launch your own Web application. And you're gonna do it in hopefully less time than you've ever thought possible. Again. Let me know if you have any questions. My name is Justin. I'm gonna be taking you through this Siri's and I want to make sure that you really understand Django, which is why I've been launching these courses for years. And it will say if you find a different version of Django and it course with that different version, go ahead and stick with that version. This is meant to be on update to the latest version. But the idea here is you shouldn't be too worried about the version of Django. Instead, what you should be worried about is the Django itself, like actually learning what's inside of Django. That's also true with python to there are certainly changes that happen, and some of them are important. But for the most part, as you're learning, those changes don't really matter. Instead, you wanna understand that concepts that buildup, what applications and how you actually do that with Django in a riel, actual live environment, Let's go ahead and get started 2. 2 Overview: Here's an overview of what we're going to be building. It is a modern blawg application, so you can look at the blog's the Your L's change Our content changes we have published times. But another thing that we have is the ability to simply edit things at at the time they are going to be published and also upload images. And all of this could be in addition, managed in the jangle. Admit the jingle Admin is very powerful, and it's very useful for us to make changes to right, so there's a lot of things in here that aren't showing up on our main page. Another thing we can do is actually a search, something like working, and we get some results. Here we see that search result and even better, we monitor those lookups right we actually look for and save those queries. So this stuff might not seem all that complicated because it's not. Creating a blawg is very straightforward to do, but to get here it does take some time. So what we discuss is how to create views for crowd. So we show you how toe list everything out. How to show Onley published items how to show all kinds of items if it's a user that's logged in. So yes, we talk about users and authenticating users. We show you how to upload images and files, um, and associate those to any given user. We also show you how to use regular forms than Jingo and Model forms and Django. Both are very useful concepts to either just have some basic data come through, or a lot of data and data that you might want a store on the database and data that you might not really depends to You. We'll talk about your else and Ural routing. That means that how every piece of this blogger is rendered out, right? So, like, here, this is a list. This has a bunch of items at him, right? That even shows us the draft items. That's pretty cool. And then we also have a way to look at the detail items. We can edit things, and we can also delete things. Now, if you're not familiar, these concepts are the same regardless of what application? Your building. If you think about it, let's say, for instance, you're building an e commerce site. You have to list out all your products. You want to see a detail of that product, and then you, as the admin might want to be able to edit or delete those products, now is saying about that on, like Facebook or Twitter Instagram. Same thing. You have a list of posts you want to see a detail of a post you can edit or delete those posts as well. So those concepts are discussed in detail, and it shows you in code as well, using the function based views. The reason we use this method is because it's an easy method to get introduced to, and it's an easy way to really understand how each one of these Web pages works behind the scenes. And naturally, we're also gonna talk about HTML templates and how these template variables work. So all of this stuff really just makes Django flourish as an amazing Web application and to make amazing revocations on your own. So so much of this is about conceptual as well as practical implementation of the Django Project. Now, when we look at this project, it does look pretty simple, right? Yeah, sure, it is responsive and all that and thanks to Bootstrap. But it doesn't seem like there's that much going on, and certainly there isn't that much going on. But it's definitely the foundation for everything else that you could build with Django. One more thing before we actually jump in is that we have all of our code open sourced, and that's all on Get hub dot com slash coding. For entrepreneurs, this open source code means that any time in this series you can jump into the code. Look at what we actually did in the repo itself. So you can see, Let's say, Francis, you needed a reference the block model. You go into this blawg models, and you can see all of the code as it exists, at least at the very end, if not in every lecture itself. So this is really important to help you through this process because you might miss a comma somewhere. You might miss a parentheses somewhere, and when that happens, it can be really frustrating. So I've open source all the code to help alleviate a lot of that problem. Another thing is, you can absolutely learn from some of these other projects, even if you don't go through the Siri's themselves. Like if you look at the e commerce project, you can actually kind of diagnose what's going on with it after you take this Siri's and really just build your own e commerce just with the knowledge that you have here. Of course, if you want more context and you want to know exactly how it's going on, just go ahead and check out that course all together. But the idea here is that we have everything open source and we walked through this entire series with you step by step and we make sure that you get it right the first time or after a few it orations. So let me know if you have any questions. Otherwise, let's go ahead and jump in. 3. 3 Setup Django and Virtualenv: So if you're on mackerel in IX, go ahead and open up your terminal window. And if you're on windows, make sure you're using power shell. So what I'm gonna do is I'm the CD into my dead folder. Now, I created this debt folder to store all of my code projects. And, as you see already, have a folder called Try Jingo. So go ahead and do make sure try, Django. If you don't have that, we're gonna see the into try Django. And now we're gonna make our virtual environment. So pip in the dash dash python 3.6 Install Django equals equals the 2.2. You can absolutely use python 3.7 or above, but certainly nothing below that. Go ahead and hit, Enter and you're like, Well, wait, wait. I don't I've never done any of those things before. If you haven't done this stuff, I'll tell you about some resource is at the end of this video on exactly how you can go about getting to this point. Right? But the main thing is, we're starting a virtual environment and we're gonna download Django. That stuff's not really that complicated. And I'm using pip envy because it's very easy to do it. Cross platform that is Windows, Lennox and Mac. Now that we've got that, let's go ahead and do Pip envy Shell and inside of the shell. I already have some files listed out, but all you should see his pit file and pit file lock. I'm gonna go ahead and make a directory called SRC, which is where I'm going to store my Django project and we go do Django Desh and Men Start Project, and this one is going to be called Try Django. And then I'll just have that period at the end there to make it inside of this SRC folder I had enter List everything out. Looks like it's It's ah, it's actually correct. So let's go ahead and run the server. All right? I haven't done my migrations yet. I'll do that in a second. Ah, but let's go ahead and open up this in our browser. And there we go. We have Django 2.2 installed started, and we're ready to start working. But one more set up process that I'm gonna do is actually add this into a project into sublime into my text editor. And it is I've already actually done it. But I'll go ahead and do it again just to show you guys how it's done. Okay, If you haven't done this 100 times already, then yeah, this will be review for a lot of you. Okay, so we navigate to are dead folder. Um, you know where we just use that? Try jingle projects? We did the virtual environment. Open this up and I'm gonna save this project as. And of course, it's the try Django project that I already have saved in there. But I'm gonna go ahead to do it again. I'll replace what I have. You probably won't see that there. Well, we now have our project going, and I might as well run my migrations just to really solidify it. So python minutes up, he Why migrate? And then we'll go ahead and do python minutes jumpy. Why create super User? And I'm to use CFE as my user name and then a whatever password because I'm working locally . Now, if you haven't seen any of what I just did before, that means that you really need to go and check out our get Hub Repo. So this report right here is where we have this Read me. This will give you the getting started guide because we've set up our system already. Um, and this guide will actually show you how to set up your system and do everything that we just did. Now, this guide is made for beginners. So if everything that I just did was, like so fast and over your head, then check out some of these other courses because they definitely introduce some concepts a lot slower than what I just did. Now, pie 30 days of python will give you a lot more of a foundation off python so you can actually build projects with Python, and then you are certainly gonna want to know HTML and CSS. So checking out the getting started with the female CSS project is a good idea. That's just a generally a good idea. Now you don't need to know HTML and CSS like like the back of your hand to to be successful in this series or with Jane you even. But it's a really, really good idea to have at least some of the fundamental basics down and then finally, we are going to covering bootstraps. So doing the boot shop basics and understanding how bootstrap works is certainly recommended as well. Those things if you haven't done them before. The reason I'm talking about them now is because I do think that they are important enough to really be successful with Django and also with this Siri's. 4. 4 What Django Does: even though the actual set a process felt very rapid fire. Like I want to make sure that you know that's Onley toe help guide. The beginners, like the absolute beginners that have never done that before, probably shouldn't be doing this whole Siri's from scratch, right? They should be getting Mawr familiarity with some of the absolute basics. That's the reason for that going forward, we will be discussing all source of jangle stuff in depth, and it's probably gonna be more this pace versus what I started with. Okay, so now that we have that project set up, let's go ahead and go back into it so seedy into my debt folder into Try Django. Let's go ahead and activate our virtual environment pnv Shell. Now I did mention it, but Pippy and he makes it easy for me to send this code to a Windows, Linux or Mac user. And you could just run Pippi and be shell or and also Pippi and be install and then always were requirements will be there, your virtual environment be created and so on. Cool. So now we got asked. The question is like, What is Django? Four. Right, so if I see the into the SRC folder and run Django. So python managed up, He Why run server? I'm gonna go ahead and grab that your l and let's go ahead and take a look at it. So in here we have our jangle project running. It is Geno 2.2, pretty sweets. The newest version. And this is where we start to discuss what Jenoff is four. Now, if we go into the admin, this is something that's built into Django, right? So jingle has a way for your database and Django to talk to each other very easily. Now, if you've ever done like a one click installed a WordPress. This is, in a way, like the WordPress admin. It's not quite the same because that's just a whole different kind of system. But the idea here is that you can change database entries really easily using Django. So let's go ahead and log in. This is that super user password that it created. If you don't remember what it is, or you don't have one, you could go ahead and just create a brand new one. So python managed up here. Why create super user and do it that way. That's super User has access to log in to the Janu and men. Of course, we have to make sure our servers running or our development servers running to get in. And then we log in So right out of the gates. General has user authentication and authorization. It's built in to Django. I mean, this saves a lot of development time in of itself, right? I just actually logged into Django. Now I contest this by going into an incognito window, and it's gonna make me log in again. Right? So my session is no longer valid. Of course. All sorts of things I can do in here I can look at these users. I can make changes to them. And those changes should stay regardless of if I'm logged in or not. Right? So if I log out, actually, let's log out and also destroy the server there, cancel out on the server with control. See, we could rerun the server now, Control. See? It says it right there. That's usually how you cancel a lot of commands on the command line anyway. Alright, so I've got the server running again, and I logging in. And what do you know? It still has my user as well as that name that I said. So it has this persistent storage and that storage is inside of this DVD dot sequel Light three file, right? So that's my actual database file right now. So hopefully this tells you a lot of what Jane Doe does out of the gates. It has authentication, which means it has security. It also stores it the passwords in a secure manner like it's not just the raw password like we have with our user name and our first name. Those are just raw data, right? So does that. It also maps to database technologies, so it easily maps to sequel. And that's called the Jingo O. R M. I'm not gonna get into that too much here, but the idea is that what you define in Django can be mapped into a database so we can access that database. If you're not aware, databases have a syntax and language of their own, so there's something called sequel that's SQL queries that you can run on your database. Django Dozen handles all that stuff for you, so those are some of the built in things about it. The other thing is, as you notice, I'm changing the pages. Django is also handling that it's handling what should be seen in any given page in this case is using the Django admin. But what we're gonna be building is not in the Django admin. I mean, we start here because its built in, but we use a lot of the features that build up the Django admin to build our own web application right to have this sort of interaction with our actual users because the jingle admin is not gonna have that. So another thing that's built into Django is something called views and Views handles how any of these pages are rendered. Now, what that means is is it actually converts a bunch of data into an HTML document, and it shows that document at any given your l. If you're unclear of what I'm talking about, no worries there. We're gonna absolutely go over every step of all that to make it our own right. But I just want to give you an idea of what it is that Django does and why we use Django versus something like WordPress. You use Django because you have complete control over every piece of this WordPress. You probably could change all those things, but it's not made that way. WordPress is made toe work out of the box for blog's Jango can make a blawg. It can do all sorts of things, and it could also make a Web vacation like Instagram. I mean, Instagram used it. I think they still use it, but it's their main back end, right? That's pretty cool to know. So Jangle has a lot of power built into it by default, and those are the things we're gonna go over. 5. 5 Define a View: now we're gonna go ahead and set up our home page. It's also known as our index page, or even potentially even called your sights route, which basically means that it's where your domain is gonna end up. And that's the home page. Hopefully, you're already familiar with that concept. Let's go ahead and create our own. Now, the reason we're seeing this is because we haven't really changed anything about Django. Once you change something about Django, this will be gone, and it will show error pages instead, assuming there are errors. So let's go ahead into sublime text, and I'm gonna go in and create my first view. So to create views, we are gonna create a new file. There's gonna be a python file and put it in to where settings that pie is. And I'm gonna call this views DuPuy. Okay, So what? Jane, you is cold. It's a model view template set up. So that's M V T. You may have heard of M V C model view controller jingles more of a model view template. No worries if you don't know what that means, but just get the idea that whenever we want to change what a page looks like. We start with a view. We don't start with HTML. We start with just a view. It's a function. It's a python function or a python class that can actually render out some stuff. So let's go and see that I'll do from django dot http import http response and they will define home page, and we're gonna return an http response. So at its core, this is what any given view function does. It is declared as a function of python function, and then it returns a response of some kind. Now, initially, I'm just gonna go ahead and return a string, which is valid HD mail, which is just 81 Hello, world H one. Right. So this is not a validation of document per se, but the string itself is html string. Right. So we've got an h one tang and we close it off with an H one tag. Okay, so another default that comes in with this view is called a request. Right? So when you go to any page like any page on the internet, that's called a request. You requesting that page to come to your computer? and therefore our view gets that request and then it returns a response, right? That's a very common thing. You request something and you get something returned. That's how the Internet works. You probably know this on a intuitive level, but that's the technical level. You get request returns, a response. So now we've defined this view. What do I have to do? 6. 6 A First URL Mapping: Now what I need to do is have a way to actually access this function like how to actually run this function on Jango like on my page. Well, to do that, we have to talk about the concept of your Els. Now, you might already be familiar with this, but what you're Ellie is, of course, in your browser. It's what? That that's an actual girl right there. And then if you change it to, like, slash ABC and then even slash cfe or just CFE like you keep changing these things, those air actually knew you or else. Now I think that you probably already know this, but if you don't what? The idea here is that this path of this address goes somewhere that your web application will start to understand. Your Web location breaks whatever that your l is that anyone requests and then it returns something some sort of response based off of what they request. So what that means is, then we need to go into our urals dot pie. So in here, this is where we configure any given your l to map up to any given view function, and at its core you will always do this in Django. You will always have some sort of your l That maps to some sort of function, and you have a lot of flexibility of what that means. So let's do the first thing, and that is actually changing our admin, your l and it's really simple. I'm just gonna go ahead and do CFE desh admin. You can do whatever you want here inside of this path method. We're just gonna call cfe admin and leave everything else the same. So with my Django project still running, right, So my server still going there? I have no issues. I'm gonna go into the admin again, right? I'm at admin right now. I refresh now it's giving me a page, not found error. That's because I moved the your l No surprises here, right? It's a really simple I move the euro, the cfe dish admin, and it changes immediately. So that's Django, understanding what the person is requesting to be able to actually make that sort of change . I'm gonna leave it as admin, because that's actually a better place to have it. And I'm gonna go ahead and finally bring in that view that we created to actually be rendered inside of our project. So whenever I want to use of you, I'm going to go ahead and import it into my Urals. So in this case, I can use a relative import, and we're gonna import the actual view function itself, which we called home page. Right? So the relative in fort meaning dot views your l's and dot views are inside of the same module. So you can do, you know, this dot views call, and then we import that actual function itself. And now we're gonna go ahead and make our first pattern. I'm just gonna literally copy what's already there. Now, you have a few examples up here. You can go off of those if you like, or we can just do that. So it's an empty string here, and then we just put this home page cool. So we've now actually created our first your l. And then we mapped it to that first view that we created. So now if we go to that new or l or our home page, you should see this. Awesome. If you don't see this or if this is confusing, l of course, let us know in the comments. Otherwise, this keep going 7. 7 Multiple Views: Now that we understand how we can create thes Urals, let's go ahead and make a few more right. So I'm gonna go and say about and then contact or content contact either one. Okay, so this path, this is the path to that. Your l right. So if I go into slash about now, it renders out the same thing. If I go into contact, it also renders out the same thing. If I go to ABC, we get a page not found. No surprises here. Now, the about in contact pages probably shouldn't be the same as the home page, right? They should probably show us some sort of different information. So this means then we would jump into our view and actually copy the views and make it a couple more times. And instead of all be called home page or anything like that, we would do about and contents. And then we can say about us, and we can say contact us, and then we can go back into are your are your l's where we want to import these views. I'm gonna use parentheses now to import multiple, and they will say about page contact page. Okay, so with that, we grab this about page. We grab this contact page and there we go. Now, if I refresh in here, of course. I saved everything I do get in the habit of just saving by default and a quick way to do it is command S or control s. If you're on Windows, their contact us and about about us. Great. That's pretty cool. Very easy to dio, right? One other really cool aspect of this, especially if you're coming from the HTML world is your not having a type dot html for each page that's rendered. If you don't know what that is, that's okay. But the nice thing about how Django set up is are your l paths are nice and clean again. Django knows about these things. Django is the one that's handling how these Europe you're all paths are running. You don't need any other technology to do it. Django. Urals. Do it 8. 8 path vs re path vs url: now's a good time to talk about the path method here. Now this path method is part of Django 2.0, and above. It's just a string. There's not really any complicated things going on here, but which you could actually dio is something called a R E path. Or if you're on older version or you're used to an older version of Django, it was the your L itself. So Ari path means regular expression, and it's gonna look just like this, right? So it's not a whole lot different, but it is different, right? It actually works differently, So Ari Path is called a regular expression. Regular expressions are very commonly used in python, and the reason that Jingo had regular expressions for a really long time is not for something that simple. Is this but mawr for something like this? So let's go ahead and just do a very simple example of a regular expression, and I'll just say Page is pages. And if I put that question mark there, it's basically going to say, Well, let's take a look if I now go to that your l and type out pages. But let's make sure we save that? I get pages rendered. Of course it's showing me about us. What if I give rid of that s Ah. All right. So this actually works on two levels. It works with it renders out to something like this. So I'll just show you about Paige, so it renders that, but it also renders the s right. So this one line renders two different pages. Now, regular expressions could get a lot more complicated than this, and we will talk about them in more detail soon. But that's what's going on. I'm bringing this up for those of you who might use older versions of Django, and you might see other tutorials that in most cases are really valid. Still, except the only difference with your els is path Ari path instead of just your l. All right, 9. 9 Your First Template: As you might imagine, returning an HDTV response like this is fine for a toy example like we've done. But in the long run, we want to actually render out a riel HTML document. It has to be really valid html to really make this thing useful. So what we can do is actually set up our templates directory. So inside of my SRC folder, I'm gonna make a new folder called Templates. So templates, meaning that I can actually put actual html in here. So let's go ahead and make our 1st 1 I'm just gonna call this hello underscore world dot html And in here I'll just do N h one tag and call it Hello world. So with that, I actually want my home page to render out that hello world from the template itself from that hte email document itself. Now, to do this, we import a another item from Django. So from de jango dot short cuts import render and what renders gonna dio is it's gonna be something very similar to this response. But instead, what it's gonna dio is take in the request and then the actual template name. In my case, it's Hello World Study, she Mel. That's the name of this template, and then it's gonna return it. So in a similar fashion to that return response, it's gonna take in the request and combine it with the HTML document and then return things back. I'll explain what I mean by that in a little bit. So now let's go back into that home, Page says. I've changed that template I get. This error template does not exist. Well, the reason for this is because we haven't actually set up Django to look for this folder here, so let's go and do that. 10. 10 Loading a HTML Template: so this right here would actually be a server error, right? This is in debug mode right now. That's why we see it looked like this. But this is an actual server error. So if I didn't set up my jingle project or I named the template wrong like if I wrote it wrong here and had a different name here, let's say, for instance, I wrote just hello dot html. That's the same error. It won't be able to find this page, right, even if I have it set up correctly. Cool. So let's set it up and simulate that error all over again. So we know what the template does not exist. Error means. So if we go into our settings now, So in try Django settings right next to your l's and views, we're gonna stroll down and skip a lot of things and we're gonna look for the templates item here So that templates configuration. So the directory or the path as to where these templates are is os path, join baster, and then templates. Whoa! Where did all this come from? Well, it's actually take a look. First of all, baster is declined, are declared right here, right? So that's showing me where the roots of the jangle project ISS that's in manage dot pie, right? And how I knew it was just templates because that's the name of my folder that's literally right next to manage DuPuy. So to actually look at this, we're gonna jump into the python shell. Someone close out the Django Server and Run Python managed up here. Why Shell and we'll go and do from django dot com import settings. What this does is gives me access to this settings file here and now. I could do settings dot base underscored er, I hit Enter. That actually shows me the path to the roots of my Django project to that base directory. That, of course, is where managed a pie is, and if you want to bring them together, you can do something like this. So let's go ahead and also important less and then copy and paste to send. And, of course, I don't have baster defined. It's right here, so I'll go ahead and say based or is equal to that setting, and I'll go ahead and paste that value again. And there's that path. Of course, I could also just check to make sure that that's a valid path by opening up a new terminal window, copying that entire thing without the parentheses. They are the quotes. You don't need them. So we see the into that, and we left things out. And what do you know? That's where hello world done. HTML is and the biggest differences. The reason I don't put that direct path like that, is you Windows users probably already know is because this is not the correct path on Windows, right, so you'd use different slashes altogether. So this just means that it's relative to this project, and it's based off of the operating system that a man, those two things in combination with each other or how you'll actually find those templates . So now that we have that setting and we've verified some stuff, let's exit out of the python shell and will run Python managed up. Why run server again? And then we'll go ahead and take a look at my template and what you know, it actually renders it out. I don't have an error. Of course, if I go back into that view and changed it to Hello. Underscore and refresh it again. I get nearer, right. It doesn't find that template because that actual template file is not inside of my templates right there. Right. So, of course I need that actual file Cool. 11. 11 Add Bootstrap: Now that I have this template, let's go ahead and bring in bootstraps. I'm gonna go to get boots shrimp dot com. I'm gonna go into the documentation and I'm gonna scroll down to their starter template and I'll just go ahead and copy this whole thing here and will paste it into our hello world. And we saved that. Let's go ahead and make sure our servers still running and refreshing our homepage. Hello. World looks the same now to make it actually look different. Let's go ahead and do div Class equals two container div class equals to row and finally div class equals two column six mx auto. Okay, close off these dibs. No worries. If you don't know bootstrap enough to know what's going on, Just copy what I'm doing. Well, explain some resource is for that later. If I haven't already so refreshing this page and what you know, Hello worlds there. So, as you see, I have a lot more code going on. It's not a whole lot different still rendering out just hello world. But there's a lot more references and this is now a valid HTML documents that's being rendered so the next question should be is like, How do I dynamically add stuff into this HTML document? In other words, how do I say change this to being a different title based off of the page that I'm on, right? So, like my about page, it should be a different title. My contact page should be different title, just like we did when we were running those examples. Let's go ahead and do that. 12. 12 Render Context in Templates: So let's go ahead and jump into the python shell to discuss something that you probably know and that is string substitution. So if I said something like ABC equals two, this is awesome. And then I wanted to actually substitute awesome with a different item we can use, you know, the format method, so dot for meant and then say awesome and we would go into a B. C. You could also do it where it's a variable name. So we would say another. And then the format would be another equals two. Awesome. Right, So this string substitution this same concept works with genuine templates. So let's go ahead and exit out of the shell and gonna run the server again. So now what I can do is pass in a dictionary here with some context. So let's say, for instance, I want to pass in the context variable of title. So I'm just a title equals two. Hello there dot a dot Okay, So I want to pass this in, and I want my HTML document to use it now. With that string substitution, we could just say Doc equals too, you know, h one and then title H one and then dot format title equals two title, right? And then that document would actually render out the correct HTML. So Geno templates or the Django template ing engine is not a whole lot different than that . But the syntax is slightly different. So Django rendered Doc equals two almost the same thing. But instead of one curly bracket you use to curly brackets and in fact, you use that inside of the temple itself. So then all we do is pass in the title being the title. Of course, you want to have a valid dictionary here. So this is actually what's being passed. And these are arbitrary, just like they are with the string substitution in python, right? The arbitrary variables here. So my title just so we don't get anything to confused here. I'm gonna go ahead and to leave thes or I'll just comment, then out as reference. So again, remember this double curly brackets. Now what we want to do is jump into our HTML document and replace this part right here with title. We say that refreshing. Here we get titles not to find Oh, it's possible that I made a mistake. So let's go ahead and save in here. We need to make sure that everything is defined and saved. Refresh. I'm getting a dock error. Yeah. Oh, it's trying to format these comments. Not exactly sure why, But let's go ahead and do that. Okay? So I got rid of those comments, and now I refresh I updated those comments rather. And now a refreshing What do you know? Hey, Hello. There is working cool. So that means that also my about in contact page can do something very similar. So let's do that. And this time I'm just gonna go and pass the title here about us and also pass it down here , contact us. Okay, so now what I've done is rendered context inside of a jangle template. That's what it's called. So if we go to about says about us still or if I change it just to about, you know about and of course, it's actually using the same HTML. So if I inspect that element there, I see that all that HTML is exactly the same on hello world. Now I think that's pretty cool. It makes it really easy to render out some of our own content inside of this template itself, in a way that I think is also pretty familiar for a lot of you who know python. 13. 13 Stay DRY with Templates: There's this concept in Web development and software development called Don't repeat yourself and it stands for, you know, dry. You'll see that over and over again when you start to build things. Now what we want to do is make sure that we're not being too repetitive with a lot of our content and further like my content right now. Well, perhaps I want more than just a title in any given page, right? So perhaps I want an actual template for my about page. So to do that, I would come in here and say about HTML and then I would copy literally all of that document, and then I would put in some or relative, you know, this is about us sort of texts, right? So now my about page has some more content, and it's different content than my home page or my index page. Right. But I violated dry so back in that view, I just violated that statement. How did I do that? Well, if we look in hello world, we see that I've got all of this stuff here, including the title, including the head. I've got a lot of things in here that are repetitive, not just on hello world, but also on about. So we want to get rid of that repetitive nous. Luckily, Jingo has something built in for this inside of templates. So we go back into our templates, were going to get another new file, and we call this base dot html. Now, this is convention. Calling it based on each detail is convention. So that means that you can call it whatever you like, but most people call it based on HTML. And what this is is like the parent of all of my HTML documents. That means is I'm gonna go ahead and copy whole hello world dot html and pace it in here. Right? So when I say the parent, what I mean is, I wanna have all of the valid HTML stuff on all of my templates, every single template. I want to make sure that has those things right. And I also probably want to make sure it has my actual title and perhaps some brand and maybe a knave bar and stuff like that. Like I want to keep things consistent across my project and having my based on a sham. El will allow that to happen. So what I can actually do is I can go into any Django HTML document and I can delete everything and just literally say extends based on HTML. Right? So you got curly brackets. Percent sign extends. Extends is crucial. And then quote, based on HTML now based on HTML again is an arbitrary name. But it is convention. So if I save this on about as well as hello world, I can now render out something in this base document. So let's go ahead and look at it. I ran out about. It's still rendering that context variable, except my other part went away. And then hello World are hello there. Both of those things are still working great, but my paragraph one way So that paragraph of like this is more about stuff went away, right? So am I about if I had that in what the heck, It's not rendering, and that's because we don't have a way to then render it inside of this template. It's rendering out those context variables, as in the curly bracket one that air that air declared in the view but also declared inside of that template. Right. So what I need to do is add another block, which is not too dissimilar to this. That other block is called a block. And so we opened the block and we in the block. So I'm going to replace this block. I'm gonna replace this value with some other value. Now, as you might imagine, if I need to replace multiple values. Well, how am I going to do that? When I write block the same name over and over Cattle. I referenced this block versus this block. Well, I actually just give it a name. So these are also arbitrary. But convention sometimes you see title. Sometimes you see head title. Okay, so the blocks themselves still curly brackets percent. So the block and then the name. So what these blocks do is allow me to replace values inside of it. You can have them empty or actually have some sort of value. Right? So I say that and if I refresh on about it, says, replace this value and it also says it on the title there. So back into my about done html, I can replace those values using that block again, and then the content itself. And then we called one block content and the other one we called head title. So block content. Whenever you open it, you must close it. And I think the reason for the opening and closing is rather intuitive, right? So I want to make sure that I'm replacing whatever that block is with what's inside of it. So I definitely need it opened and closed for that reason. And now I can say just about us and refresh What do you know? This changes, and so does this. So this is called template inheritance. This is the main parents document. And then every replacement is being done by the child element. Right? So it it looks for that main document, and then it looks for individual blocks in there that can actually render out and change, and not to worry. You can still use those context variables, and you can use them as many times as you need. Right, So those are still coming in. You just need to make sure that whenever you need to replace content, you put it inside of one of these blocks. Now these blocks can render out to any kind of content. So if you had a Java script and you wanted to put that into a block, you totally can. It just replaces what's on that HTML document. And it all does that because of this function. So this function is handling all of that for us, right? If I tried to do this same thing with http response, it would not work. So it it handles all this, and it allows for much more dynamic templates. Now, if this isn't fully sticking yet trying a bunch yourself, we're gonna do a lot more with these things. These air, fundamental to a Django project, is having these templates working. 14. 14 Rendering Any Kind of Template: before we move on from this concept, I want to say that you don't on Lee have to render out HTML. You can render out other kinds of things for this template engine. So let's go ahead and take a look at what I mean by that. And I'm still going to render out HTML. But you can try it with a different data types, like a txt or something like that. So I'm going to just change his contact Page two example page. And what I want to do here is return the http response and I want to return the render data . Okay, so I want to return something here, and that's gonna be a string so that something here is gonna be equal to the results of a template. So in our case will stick with hello world and also some context. So the context or the things that will be rendered all leave in as what we've been using. And this time I'll just say example. Okay, so I want to turn this something here into actual rendered template and return it as a response. We get rid of some of these comments here, and let's go ahead and import this example page into our Urals. Okay? And then I will put this down above the contact page and we'll just call it example. And let's go ahead and look for going to example. It renders out the string of hello world. No surprise here, right, Because we didn't actually do anything other than said it as a string. So how do actually turn this into a template? Well, let's go ahead and change something here to templates, name and the context. These are now better suited for long term development. Because these air convention and would also do is go from Django template dot loader import the get template method. This get template method then is going to go ahead and say template object is equal to get template, and that's gonna be of this template name. So, whatever that is, and then rhe response is going to be template object that render. And in that context that we had here, we say that refreshing here. And what do you know? It actually allows me to send this data end as I need. That is pretty cool. So this is roughly doing the same thing as this, but it's not quite there Now. I just want to show you this because it is a good idea because every once in a while you might have a txt or a string of some kind that you want rendered. So I'll go ahead and do the last thing of rendered item. And this is not necessarily going to be convention here. Neither is template objects. But the idea here now is this is actually a rendered string so I can take this same concept and use it inside of any of my pages. Let's go ahead and say that this is my title dot txt and we come in here and say Title and that's it, right? So it's not super complicated here. But now what I can dio is leave my title in se context eagles to this dictionary here, and I'm gonna go ahead and still pass. That title is something different. This is very redundant, but I wanted to show you the example. So the template name this time is now title dot txt so we can grab that the rendered item or the rendered string in this case is going to render out hopefully something that we want to see. So we bring this down here and turn it into rendered string. And let's go back into our home page and it's still saying hello there. And I can print out that rendered string as well. So we refresh and looking our terminal, and there it is, printed out inside of our terminal. Okay, so, naturally, this is a lot of redundant information. But if my title dot txt had, you know some other data here that we really needed to render out, you could do that there as well. So yeah, I don't think what I just showed you is super practical, this part at least, but understanding how we can render templates in other contexts that is absolutely, absolutely practical. 15. 15 Template Context Processors: another aspect about the Django template ing injured. That's really nice, And it's not necessarily how things are being rendered, but what can be rendered. So I'm gonna simplify this home page again and literally just use this context here. I don't need any of that old stuff, so I'll just leave it like this and let's go and take a look at it. Got my server running and it refreshing here. Of course, it's not actually showing anything, so let's make some modifications. I'm not gonna use hello world anymore now to go ahead and use home dot html so to make a new file for that home, that HTML The process, of course, is thes curly brackets with percent and extends based on HTML. And then we add in a block contents of block content. And that, of course, is because are based on HTML has it in the media in block. So again we can have our title come in. Another thing that I can add is our user, so you can literally put user here and I can also do user dot is authenticated. And before I check that, of course I'm gonna save everything I can go my view and see that I did not pass that in as my context. Right? It's just coming through. So that means that here I can refresh. And what do you know? It shows me my user name and true. And of course, if you want to actually long in like I'm not in my incognito, it shows me anonymous user and false, right? Pretty cool. So this is because of Jang Go's context variables to look at them inside of our settings And those contacts variables, air coming from the context processors. It's coming from these things right here, right? So one of them is the off. Another thing, by default that comes through is the request. So in my template again, I can also do well. I can use request that user, which will give me the same values that and I can also use request dot path and we can refresh. And what do you know? It's showing me that same information that's related to this. That's pretty cool. So those are Django context processors. They're they're added by default. So on our views, we don't have toe re reference them, and this is something that's like used over and over again so that we can stay nice and dry and not have to add in our user into context every single time, Right? That's pretty cool as well as our request. 16. 16 Template Tags: So we've talked about the very basic template tags. This is a temple tank. This is a temple attack. This is a temple attack. We can do other kinds of template tags if we want. Right, So there's a condition. So if user dot is authenticated, and then we can actually execute code in there, So if the user is authenticated, then we can say, you know, user Onley data or specific data for that user, which will get into later. But for now, user only data is pretty nice, because that means that if I go to my incognito window, I only see this rather if I go to my user that's logged in. I see all that. Of course they're logged in through the admin if you're not clear on that one, So another thing we can do is loop through things. So in this home page, I'm gonna add a list, and I'll just call it my list. And this list will just be very simple. It's just gonna be 12345 This is now a context variable. So inside of home dot html, I can loop through that context variable again using the block. In this case, it's four. And then some arbitrary variable here. So for a in my list, and then whatever you open, you must close. And then, in this case, I can just do a list element and literally render out the very bowl A right. So this loops through all that stuff, and it's only gonna loop through if the user is authenticated. Right? So here it is. And here it's not right. Okay, cool. So that's a few of the built in template tags and filters. There's a lot more, right. I'm not gonna go through all of them. There's a lot on the Django docks that you can kind of play around with yourself. Um, I did the if statement I did the four loop. Now I will say that every once in a while you're gonna wanna have something like this in your templates. But most of time you're gonna wanna do that conditional logic. And here, in other words, I would say something like, if request not user is authenticated, then this is what the context will be. Otherwise, the context will just be this right. So this is where you're gonna want to put that kind of logic and then that way in here it will only run this loop, right? So I get rid of all this. It's only gonna run this loop if there's values to it, right? So coming back in here and refresh, there's that stuff coming through, whereas on my incognito window, it's not there, Right? The title did change because it changed that context. But it's not gonna loop through if there's no value and therefore in your actual views, this is where you're gonna wanna handle that sort of logic, right? So whether it's a list or any other sorts of elements, you're gonna want to think of it in here. But this comes with time, like after you practice it a good amount. Then you'll start to get a better understanding of when to use what so my argument would be used, what works, get it working and then start to refine how it's actually working. But it also helps by going through the Django documentation for these built in template variables and actually playing around with them yourself. 17. 17 Your First App: Right now, our site is aesthetic. The reason it's standing is because we declare everything and it's not really being updated or changed by anyone, right? Even the admin has nothing to do with what's currently going on on our different views as well as the Urals. Now this is fine, if that's all you really want. But Django really shines when we start making it dynamic that is, having data stored in a database that we add to and we can actually see. So there's like that interchange of data, right? So, like think of like Instagram. For example, when you post something you're adding to the Instagram's database, Django does that really, really well. Which is why Instagram did use it right, like or has used it in the past. So if we jump into our settings file right, so strolling down a little bit too, installed amps, these are the things that make Django that much more dynamic. And there's a lot of things built in by default, right? We've already seen the Janu admin we've seen are off users. So our actual user that's logged in, right? So this user right, we've seen sessions, two sessions are. If I open up another tab, I still see that user in there, right? And we can also log out that user with sessions. There's a few other ones that are not gonna cover right now. But the idea here is that Daniel has a lot of this stuff built in and they work out of the box. But what if we wanted to add our own thing and that's what we need to do now. But I will say that they're called APS, but I like to think of them as components. Right? Say these air, all little pieces, that buildup toothy Django project as a whole, right? Don't think of them in terms of APS like your mobile APS or the fact that this entire project could be called a Web app like don't think of it in those terms. Think of them as little tiny components that build up your larger project, right? I think that's a better term than APS because of just what convention For absence. Now everyone thinks of APS in terms of your mobile phone, but when it comes to Django, the ABS are the components and these components or these APS integrate and can integrate into our database. And that's what we need to dio. So it's going to our terminal and we're gonna close out the server here and we'll list everything out. And I'm just gonna run python mayors up high and hit Enter. And what we see here is there's a bunch of Django related things. Um, you know, we've seen my great we've seen start project. Now there's the one that we're gonna use. It would just start app That's really useful if you forget what things are. So python managed up. Ey start app and I'm gonna call this blawg. We're building a blogging application, So I'm gonna call this app Blawg, and I want to keep it very, very narrow in focus, right? So really, it would probably be better to call it Blawg posts because it's related to all of my block posts. But I'm to keep it as blawg because writing out blawg underscore posts over and over again , just gets very tedious. So let's keep it in his blawg. And there we go. So now if anyone went to my code, they would see that Hey, I have an app called blawg and in this it actually already built a bunch of things. For me. It has a models file. It has a views file and as an admin file, those are the three main ones that will look at. We will also look at Test two. But the main thing here is understanding that, yes, now we have something called Models, and what models do is it allows us to store data in the database in a very specific way. Let's look at an absolutely basic version of that, and I'll say class and this has to be a classic can not be a function. And what we're gonna call it is simply blawg post in its models dot model. Okay, so what this does is it allows us to declare fields that are going to be mapped into the database, but more importantly, something that's logical for the data that we need, Right? So if I said title well, that certainly makes sense for a block post, and then I need to declare what this field is, and we do that with models dot Let's just call it text fueled for now, that's it. That's all I'm gonna leave for my model. So now I've got a new app in a new model. The rule of thumb is whenever you change models top I. You have to do at least two things. One of those things is inside of settings. Make sure that your app is there, right? So after you do that once, you probably are good, right? The other thing is, we need to ensure that this is in our database. And also, any changes that I made to this are also in our database. So these two commands will be what you always always run after models up high. And that is python managed up high, make migrations and you might see something like this, right? So it made migrations. The next thing you would do is python managed up ey migrate. So those are the two commands that you'll always run, make migrations and migrate. So if I wanted to add something here and said like content, save it, that someone would go ahead and say Knoll equals the true and blank equals the true. No worries. We'll talk about that soon. Let's go ahead and save it. What do we do? We run, make migrations and then we run migrate This insurers that what's on our model is also what's known in the database. So the database now knows about this model and we can start adding to it, but how do we do that? 18. 18 Save to the Database: What I'm gonna do is some really quick python class understanding. So I'm gonna make a new class here and just write Blawg. I'm not gonna pass anything. You can pass the object, but that's inferred that it's an object. So you can write that if you like, or you just leave it empty. Also a title Eagles to hello world and content equals to something cool. Okay, so this is not Django. This is just pure python. So if I go into the python shell just plain old python shell, no need to do managed up high. And I copy this in here, and I run over J equals two blawg o b J two equals two blawg. With those parentheses, I now have two objects, also known as an instance. So BJ and over j two Those are also instances of this blawg class or this block object. So if I do O b j, I should see something like this. If I do o b j dot title, I should see that o b j two title, right? We should see all of these things and then o b j dot underscore underscore class. We should see the actual class, the same as what we declared for it, right? So Jingle models aren't actually a whole lot different than this. But instead of retrieving the data after we initialize it, we can actually use it in the database. Some of the words if I said Obie J two title equals to another title ended O b j two dot save that would then save it in the database. Of course, this default class doesn't do that, but Jingle has that in by default. So if I do it o b j two dot title, of course it's showing me that title. But if I exit out of here and go back into Python and try to type out o b j dot to, it's not even to find right. Blog's not even defined, like none of those things are defined. Now, of course, I assume that you know this if you don't know it, that was a nice little introduction to how classes work. If you do know it, it's a really easy way to save data into the database. And so let's get rid of this example. Save everything here. I'm gonna exit out of this Python interpreter and then now go into the Django version with Python. It's up, ey shell. And then we're gonna go ahead and import this class. So do from Blawg Dumb models, imports blawg or rather, blawg Post. Right? So it's that model. Now, if you're not familiar with Hannah, get here. Let me know in the comments. But it's really simple. Were starting this and managed up eyeing. So the roots of the jangle project, which, if we break everything down python, Mickley, python managed up ey can go into the blawg with from blawg, right? So that's a python module itself. A block module is in there because that in it file so we could do from blocked out models import that blood post. Now I can go ahead and say, O b J equals two blawg Post over Jada title equals two. This is my title and obey Jada content equals two. This is my content and then Obie Jadot save. This time it's saying that it's missing the requirement for self. Of course it is. I didn't initialize that instance very well. And there we go. Now you go and save it. And what do you know. Does it seem like anything happened? Well, let's exit out of the shell and go back into the shell, import that again, and then we're gonna do a query. So we're gonna actually look in the back end and I'll say, Oh, BJ eagles to blogged post dot objects not get title eagles to, well, this string right here. And if I do over Jada title, there it is over Jada content. And there it is. Right. So what did I do? I actually imported that object, created an instance of it correctly here. And then I saved that instance into the database and then exit out of the shell and then went in and retrieved it from the database, so I can do that also with the Django admin. 19. 19 Model to Django Admin: so I was able to save this data. Let's go ahead and add the blawg model the block post model into the admin so I could do it there. We're gonna go ahead and do from dot models, right? So the models module and the admin module are in the same module, so you can dio a relative import, and then we do import blawg post, and then we just do admin dot sight that register block post. That's how you actually bring it into the admin that has been convention for a long time. And that's how you do it. Okay, so our model is named block post. If you named it something different completely. Okay, typically with models, you don't want to name it with a plural because you'll have a lot of instances off that object or that actual model like we kind of started doing. We already have one instance. So it makes a lot more sense to do block post. Okay, so this adds it to the admin. Let's go in and take a look exit out of that shell and I'm gonna run the server again and in our projects will go into the admin. And what do you know? Got blocked posting here. If I click on it, I have my first block post has title and content. Not a whole lot of data going on here, but that's pretty cool. The next thing is, I'll just make a new one called Hello World as my title and my content being just like ABC . Save that I now have two objects in the database. Let's go ahead and query the database for those objects. Let's go into the shell so python manage DePuy shell. Go and do from blawg. The models import blonde post and we'll go in and say, Oh, BJ equals two blawg post objects that get title equals to Hello world. I'm going off of memory here I hit O B. J. I don't actually have a title matching that query, so maybe there's a lower case. W ah, looks like that worked so I could do oh BJ title. And sure enough, there it is. Now there are more complex ways to actually look up this data, which will get into later. But as you see, it has a way to say does not exist and a way to actually get it if I did the right query. Now another thing that's built in to all models is an i D field, so b j dot i deem. That's what these numbers signify. It actually shows me the i d off that object that gets automatically incriminated every time you save a new object. So another let's go ahead and save another object will say O B J two or rather O B J three angles to Blawg Post and then we'll initialize it over. J three dot title equals two. This is awesome again or something like that o b j three dot safe. Unless I did not save or put any content in there. And now I can exit out of this and let's press up a few times, run that server again. Refreshing here. And sure enough, there are my three objects. So now that we have a way to store data in the database, it's time to actually view this data inside of of you 20. 20 Model in a View: now, I showed you some methods on how to actually look up this data in the shell, right? So if we click on any of them in the and men, we see that there's that number there, right? So that's that I d. That it has by default, and every single one of them has that. So I actually want a reference this block post. Now, from this I d. That's how I'm gonna actually grab this data. So let's go into sublime texts and into blawg views, and I'm to go ahead and import the blogged model. And it's a relative importance, like a new from Don models important blawg Post. And the object I want to grab is the block post that objects that get. And I'll just say I d equals toe one. And I know that that exists because if I go into the admin, I can see that there's this object and the idea is actually listed there, and it says one. Okay, so now what I want to do is actually use this data interview. You may remember we to create a view. We define a function here, and then we'll just go ahead and call this blawg post detail page, and it takes in request of the default and the returns render request some template, name and some context. So let's actually make those as variable. So it's a template, name and context, just like that was a context dictionary and a template name string. So what do we even called the template name? Well, hey, why don't we just call it block post detail dot html? Actually, I think that makes sense. You call it the same thing as the view itself, right? With the exception of the last part. But now I know I can infer that this is absolutely a template, and then I just crab in this query, all right? And the nice thing to is I can actually pass in the entire object into this context. Right? So what I mean by that is I don't actually have to do, like, object title and say title is equal to that. This passing of the object means that in my template, I can actually unpack what's inside of that object as well. So to actually make this work, of course, we have to create the template. And then what else do we have to dio We have to add this view into our Urals. So inside of my templates, I'm gonna go ahead and make a new file in here and call it blawg underscore Post on the score detail today she Mel Oops. I mean, got to make sure it's the correct extension. Okay. And if it wasn't the correct extension, it would still most likely render. Because as long as the template name is right and it's treated as HD male, it should work just fine. So as a reminder, we do extends based study html and then we put in our block content in block. So back into our view. The Context dictionary tells me the context variable is object. So now I can just do something like H one curly brackets object a title H one and then maybe like a p tag object that content. Now, if you don't remember these, the dot notation dot whatever is coming from the field names, right? So I have to fields in here, so that allows me to do that dot notation in the template as well. It's not really any different than what we were doing in the shell when we were doing O B j dot title right from the same thing. Okay, cool. So they got that. We've got a template we've got of you. It's time to add this into our your l's so into our your l's. We will add this view. So from blawg dot views, import and import that detailed view for that block post detail page. And of course, it's not a relative import this time around. It's relative for the project, of course. So I have to actually go to the block module itself and then import the views like that. And now what I'll do is at another path year. And I'll just call it Blawg. You know, render out this detail page So we say that and let's go look at it just going into blogged and sure enough, I got this is my title. This is my content. I want to change that. I go back into my view and I changed the i D that it looks up by than a refreshing here. Looks like it's the same content and then the 3rd 1 is different content. Cool. So we now have a way to actually look at that detail. It's really not that complicated. It's pretty cool. And we also know that this database that's driving this data right so this query right here , that's what it is. It's called the query that goes into the database. The database returns the data and then Django renders it. That's essentially what's going on here critical. 21. 21 Dynamic URL based Lookups: So if we look at our Urals now, we see that the blonde post detail page is sort of static, like it's not dynamically looking for this, right? I actually had to set this value inside of the view itself. So what I want to do is have a dynamic your l. So my view is also dynamic. In other words, I want to be able to pass an argument with my your l and then have my view update based off of what the argument is and using the euro pattern for path. It's actually fairly straightforward. We put in these brackets here and then we say an integer and I d So you say what? The data type is like a python data time. And then, in my case, it's gonna be an I D. So when you do that, this actually passes in a new additional argument into your view. In this case, we named it I D. But if I called it post i d, then it would pass in post i d. And then with that number, whatever that is, we could add it in to our look up. Okay, so now with these two things. It's a lot more dynamic. We look in our blogged that your l it's no longer rendering out anything. And of course, that's because we changed our actual blawg to have a dynamic item. So let's look back at that. Your l right? So if I wanted to have a another way of doing this right, so I'd actually want to have a list of items here at some points right to handle that route . Blawg path. I believe they commented out for now. But if we go back in here, I can change my your l to have a number. It's a one or two and so on. Of course, if I said ABC, it automatically infers and says Page not found, it doesn't even try to go to that in point because we declared that data type. So if this was a string, it would give me some other error, right? And this error is because of our look up. We'll explain that in a second, but the idea is if we want this to be sort of strict to a data type, we would use an integer, and that means that then something like this will go to page, not found where something like 500 will give me a does not exist error, which is no surprise. I don't have 500 items in my database at all. I only have three, so the max value would be three. Cool. So that's how we can dynamically load these views now, of course, it doesn't have to actually look up anything, right? I could just make it where it's on Lee showing whatever that number is. But it makes a lot more sense. Haven't look up something and notice that are your l's really haven't changed a whole lot. We now just have a dynamic your l here before we go away, there is another way to right this and I'm just going to show you it. Don't worry if you don't fully understand. But if you were to use regular expressions, which is what you'll see ah, lot with my stuff. It would be something more like this post I d flash D plus and dollar sign it yet. Okay, so these two are actually representing the exact same value and the exact same response. So the regular expression right here is looking for Onley digits, only numbers, and it's going to render to this keyword value. This is just a shortened version of that, and it used to be inside of your L. This is one of those minor changes in Django that's arts major, but if you're a beginner, they could be rather frustrating. But that's something that's important to note, especially if you watch older versions of stuff pretty cool. 22. 22 Handling Dynamic URL Errors: We just saw an error occur that we don't want to write. So if I have str in here and change it to ABC, this is an error I definitely don't want to see. Right. And the same is true. As if I went to integer here and I just did, like 1000. I don't want to see these errors. These air actually server errors. So if this was live, the user would be like, What is going on here? This is not correct. Well, there's a couple ways on how I could go about solving this, right? I can go the hard way or the long way. And that is first and foremost going from django dot http, import http for four. So what this allows me to do is raise what's called a 44 page so I can literally put this object, look up into an exception handler, and just literally raise 80 before four. Right? So with that, then I get this page not found on those two items. Assuming that this is back to str All right? It's giving me a page not found for either one and then the ones that are valid it won't give me that patient found. Okay, So usually when you write exception blocks, though, you want to be mawr explicit to what the actual exception is. In other words, let's take a look. And another exception. If I do 12 we got block. Post matching query doesn't exist. This is a do not exist exception or does not exist. Exception. So we would actually want to handle that exception itself. So be blocked. Post Don't does not exist, of course, is the model name that does not exist. Then you would handle that exception. So I it's get rid of this example here. Refresh. And now it's doing that page not found. But if I go back into ABC again, it's giving me another exception of value error. Okay, so what would I do? Let's accept value error. That's a standard python error. We say Raise HCP 44 and sure enough, it doesn't OK, but as you might imagine, this is starting to get cumbersome, and it's probably not valid. It's probably not a good way to actually write this view, but it's important to see that errors happen, and you do need to back up and try and diagnosis to what's going on. You will absolutely need to do that in the future. We will show you a way to shortcut this, but it's important to see it and also understand that when you're designing your your l's, it's also important to understand what data types are gonna be being passed in your your l shortcut like this, right? So this is a dynamic your L. But it's being strict, and it's saying it's only allowed to be integers. Otherwise, it's going to raise that 44 air for us, right? It's It's going to do this on our behalf because it's on a valid your L. That's also true with the regular expression value as well. 23. 23 Get Object or 404: So I look up here rendered regardless of what the data type Waas. In other words, it attempted to render when it was an str and in some cases it actually worked. In other cases, it did not right. So when it was ABC, it doesn't work. It's his page not found, but when it's one it does work. Let's actually take a look at the data time that's coming through by doing print post. I d dot underscore underscore class. So we're looking at the class that's being passed in here. We'll refresh in our view. Let's make sure the service running the classes string right. No huge surprise here because in the u. R L it's a string. What if I change it to int refreshing here? The class is integer from, so it does actually have that dynamic class being rendered. But my look up, regardless of what the class waas is still trying toe look things up right? So if this was an imager or a string, it would try and look it up, and if it is a string, it would actually work. In other words, if I said str here to make it a string as far as a look up is concerned. Regardless of what my your l says I refreshing here, is still working right, because what Jang was gonna end up doing is say, Hey, this is an I d field or any of these fields can be lookups. The I D is I d equals two models that interest your field, right, so it's automatically gonna look it up based off of that. But when it queries the database a string of one and the number of one, as in, you know, let's look at the python, right, So one is one, and then the string of one is also considered one in this look up because the string of one Times 200 gives you this where the one times 200 gives you 200. So both of those things are interpreted the same way. When you do this, look up, which is why we're also getting that value error. It's gonna try to do that look up, regardless of what the actual data type is. Of course, I actually want a more streamlined way to do this, and that is get object or 44 So in this case, I'll say object equals to get object or, for four, the name of the model itself and in the field. I want to look it up by which, in my case, was that I d field at this point. Now I can actually get rid of this and give it to that class call. We say that refreshing here after we run the server game once exit out of that python shell , and sure enough, it loads that if I go to some page is not found. It will do that if I go to ABC. Well, I still have the euro as an integer, so it does page not found. So this is This is absolutely valid, as it is now, yet again. If I do str here and then refreshing here, it still gives me that value error. This should be, hopefully have no surprise because that's not a valid value. Four. An integer field. It is a valid value for a text field or character field, but it's not valid for an intruder field. And then therefore we would probably wanna handle this in another way, or you just need to know for sure that Hey, when I'm doing this kind of look up, I need to make sure that this is an interviewer field. Okay, I've seen that happen a number of times where these lookups were invalid. That's why you want to play around them. So let's let's look at how we can do this in a little different way, too. 24. 24 A New Database Lookup Value: So what did they actually start with the look up of being I d. Hopefully you were wondering that if you weren't It's really simple. If you go into the admin and you select any of these items, you notice that there's a one in the your l. If I change that one to being a to it changes the data. If I change it to being a three, it also changed the data. So the Django admin actually works off of this same sort of methodology. But that's actually not where I wanted to be. I don't want my project based off of how the admin is. I don't want a based off of an interest your it all. Instead, I wanted to be something closer to actually ABC or like my blawg post that I actually much prefer because, well, the Urals a lot easier to read. Therefore, it's a lot easier to remember. Or if you had a block post called Getting started like Hey, I can pass that your l a lot easier than that was Object 3 to 41 or whatever, right? So that means that we need to adjust how our view is going toe. Look up our data. Whether it's this method or this method, we need a new way toe. Look up this data in our database. Now, if we look at our model, these air, the three fields that are in there by default the I d field is also known as the primary key. So both i d and dot PK both of those things will work. We have a title and we also have content, so I don't have a lot to look up off of. So what I need is something called a slug and we do models that slug field, and I'll leave it like that. So the slug itself has to do with this right here. So slug is a U R l encoded value. So with your l's, I can't do like something like Hello there. This is an awesome you know, dollar sign. All this stuff like that's not a very valid your l. In fact, even the euro itself uses thes percent sign percent 20 to replace the actual space that was there. So slugs fields do that, too, but they replace it to make it look a little bit better. So hello. World becomes something more like hello, world, or that's actually a valid slug field. So now that we've got this, I've added it into my model. What do we need to do? Well, I made changes to my model, so I need to close out my server. Python managed up here while I make migrations. And then Whoa, what's this? This is a new error that you may or may not have seen before. You are trying to add a non knowable field slug to block post without setting default. So this is Django doing a test to ensure that when you make some changes to your database that the database knows how to handle those changes? Another word. I just added a new field, but I have, you know, three values currently, right? So what do those I need to put for slug? Right for this new field, what do I put for them? And Django, by default, is saying it can't be no right, so it can't be empty in the database. If I wrote Knoll Eagles, the true it's not gonna have this error. If I Another way to do this would be say, default equals two. This is my slug, right? I could set a default value to either one of those. If I said that, it wouldn't have told me to do this. And of course, I do have a one off default that I can set for all existing rose or otherwise all values that are currently in your database. You see, when you create databases, you want to make sure that Django and the database have the same mapping, right? And when I say mapping, I mean this maps of database, this maps of database, this maps the database in Django by default is saying, Hey, this isn't in the database. You're adding it to a pre existing database. We need to know if you can make it and all in the database or just not even in the like, the actual value itself can be empty in the database. Or you said, a default for the actual value in the data base for everything that's currently in our database. So if we had 10,000 items in our database, this would set all 10,000 of those in our database or if you said no equals the true all 10,000 of those would also be empty. They just wouldn't be there. Cool. So what I'm gonna do is I'm gonna set a one off default. I'll just say one. So that's my option. And now I'll go ahead and say Hello, Dash World. Okay. And enter. I added this field now Iran migrations, but I didn't do the other part, which is by the managed a p Y migrate. So now it's in the database. Okay, so it's going to take a look at our admin. Let's run the server again and we look in here and what do you know? We've got a slug it in each one of these items and they all say Hello, world Great. So let's go ahead and change our view, or it rather are your l and view to be in str and it's gonna be the slug. Now it's no longer going to be the Post I d. So one quick way to do that with regular expression, something like that. Okay, so we've got our slug here. Now it's our view. This should be slug. Actually name it slug. And then now our look up is no longer the i d field, but rather the slug field. So we dio slug equals to that past argument a flug. So those are two examples of that look up and now it doesn't look up by the i d. At all. It only looks up by that slug field. So naturally, when we go to this one, it's going to say not found when I goto one not found right. But as soon as they go to Hello, Desh world, as in the value that is set by default for in my case, three different blood posts hit Enter It gives me another error like what the heck, What's all these errors all about? Well, this is actually a good thing, and I'll explain why in a moment. But the idea here is we've changed the way we actually look up this data in the database 25. 25 QuerySet Lookups: multiple objects return. This is a common error that a lot of Django beginners see, and that's because the lookups aren't always exactly clear how we're gonna do it. So let's go ahead and take a look at what I mean. We have two kinds of lookups, this one in this one. They're doing the same thing. Except this one just renders for four if it's not found, so the key part of both of them is this. Get call. So get a plot like implies that there's gonna be one object, whereas filter applies that there's gonna be a list of objects. So filter is another way to actually narrow down some content. All right, so let's go ahead and say this. We've got O. B. J equals two objects, not filter. But again, it's not one single item. Instead, what it's called is a query set that is a very common term in Django. This right here is a quick reset. It's going to filter out my entire database for this matching value. In other words, it's searching the database for this value being matching so you can change the field that it's looking for right. We could change it based off of title content, whatever and anything that matches that will come back in. This query said. So let's take a look in the terminal. We're gonna open up the the Django Shell with python minutes, not people I shell and they will do from blogged up models, imports blawg post. And we said our slug waas Hello, Dash world. So again, we do block post objects that get slug goes to slug. What should we see here? We get the multiple objects returned. So we press up and change Get to being filter. We get back what's called a queer set which has all three of those objects. Not a huge surprise here, but this is filtering downwards in our database, so we can now see them. So one of the ways to compensate for this error is to say qs equals to that query, whatever that is. And then we could do qs dot first. So this is a method on a query set, like literally a general quarter cent. This will grab that first object and it will give us back something right with that same thing being said, you can also do last And since it's a python list, we can do some sort of index value. So this would be the second item, right? Because we have, ah, full list here. Okay, so these query sets help account for our slug being a problem. Like if we have that same slug over and over again. So what I'm actually gonna do is all exit out of this and we'll run our server again. Now, one of you is saying, if queers or if the query sent that count, this is another method. You would use that instead of the length of query sent for those of you python users. If Cruz said that count is not equal toe one, then we would raise and http 44 that sort of one way to handle that. And then we would just say our object is equal to the queer sent that first. Okay, so this would handle that error for us. And if it is the same error, right, it's going to say 44 not found. So that's one way to handle that. Pretty set. Another way would be to say, if it is equal to one, then we can give that object being that or we could just say, if it's greater than that's greater than or equal to, that, we can just get whatever is first and then query set, and then that way it will ignore all of those other values. That's a good and bad thing. It's good in the sense that, hey, it works. And now I'm not getting these weird errors. It's bad in the sense that now I'm missing content from my page. I actually have to other posts of the same slug, so we probably need a better way to ensure that our slugs are unique. 26. 26 A Unique Slug: So this seems to solve a number of problems for me now, One of them that it still won't solve is an invalid data type. So if I change that to I D, I'll still get this invalid data type. That's definitely not something that's gonna solve now. But what it does solve is the same object with the same actual look up in this case, the same slug all across a few different objects. However many what it also does is it will still whom we get this error. Of course, the reason I'm getting this error is because I don't have that objects set up down here. So what I would want to do is just say else, raise HCP 44 right? So it's It's basically saying that. Or better yet, we would say if it's equal to zero, as in there are none. Then we would go ahead and raise that error. Okay, great. So now we've got that. So it works on hello world. But of course that slug is redundant. We've used it multiple times, so there's another way to actually fix this, and that is to change our slug from just being anything can do it too unique, equaling a true Now this might be something I want to actually put into my database. Before I saved this before I actually start the migration process, I want to go into my database and change all of the current values of hello World, all of those non unique values. I want to make them unique because now that I've got this, I can close out my server. Python managed up. He y make migrations and then Python managed people. I migrate now, if you didn't change your database, you might have a bunch of different errors come up there. So that's why it's also important to do a lot of these things testing locally and get this model really right prior to actually releasing this in a real production application or a real world application. So now it's a little bit closer to what I want, and I can now bring my view back to being just that object, because now that my slugs are all unique, it's better fit to this idea of energy field, right? So those are all unique to default. That's how they work. But now all my slugs can only be unique, which is even more clear when we run the server again and we go ahead and add a block post here. I'll say New Post and I try to use Hello. Um, world and I had saving. Continue. I get a validation air. This called a validation here That saying that this is not valid data because this already exists, right? We said his unique it had to be unique. What if I just leave it empty? Hit, saving. Continue. And it says this field is required. I told you, explain these two values this new value this field is required that's related to this blink . Right? So if I change that to blank being true, I'm not gonna leave it this way. Ah, but notice that I can now have it has an empty field. Of course, I don't want to leave it that way because my database cannot be knoll. So we want to make sure that that is blank equals the false, which is the actual default cool. So now that we have the unique clause, it's time to actually build out mawr of our views 27. 27 CRUD and Views: Whenever we go to a web page, we are requesting data from that Web page. This is called a request. Django treats it as the name request. So we can also see when we request a Web page by literally reloading a page, we see how many times we reloaded it and what's going on. Right? So it's doing a type of request and to a specific your l right. And if we back up to the home page, we see that it's requesting that page that right? And then we refresh and so on. So what's actually happening here is that request goes into the Urals, right? So the user is looking for a specific your l that you're l is then sent to Django Django, then parses that your l and looks in these patterns for any matching patterns. And if there is one, it's gonna then send that request to the view that handles it right. And then, from that view, we can return some sort of response or no response at all. But most likely, you will return a response. So it kind of looks like this, right? We got a request goes into Django and then Django sends back a response. And so Jane goes the server in this equation. Now our requests actually have things attached to them. So if I actually print out request dot method Andi, I'll just go and say, Django says. And then we can also print out the request dot path. I can also print out requested that user. So the request has a bunch of things attached to it by default from Django. So we look at this. I refreshing here several times, and what I see is Django says this stuff right? So it's a get request. That's the method type. And then we've got the path and then the actual user. Now why is it that I'm telling you about this? Well, when you typically go to grab data, you're typically doing a get request. It get requests means that you're getting data you're grabbing whatever that data is, and that's often in the form of grabbing it from your database as well. So there's no coincidence that grabbing one single object waas block post objects that get that is somewhat correlated here, right? So it's still getting whatever that object is. So there's a concept in Web development altogether called Crowd. So what Croat is is it takes this same concept of git but expands it to several other methods that are in your standard Web applications anyway. So we just talked about gets That's one kind of method that is to retrieve or even toe list things, get into the list stuff later. Then we have another method called Post, and that is to create things. Now let's go ahead and see a post method. If I go into the admin into adding a block post and I'll say another block post and blawg post another or whatever, and then we had saving Continue says he's made. If we look in the terminal, we see a bunch of get requests here for our static files. No surprise there. And if we scroll up a little bit, we also see a poster request. So that happened when I actually submitted a form, so that's when it was actually created. Now, on this thing here, I can also update this form. I can hit, save and continue, and it says there's changed successfully. I can look back in the terminal, and once again it's a post method so I can use that for of date as well. And then what about when I want to delete something? So let's go ahead and just delete this item. I'll say yes, I'm sure I hit delete and yet again, another post method so I can use post for all of those as well. Now, there actually are literal, http methods for update and delete, too. And I'll talk about those just briefly later. But we now have the basis of a web application that strut so that stands for create, retrieve, update and delete. So our admin has these crowd views already, even though it might not have seen lie yet, Right? So when I come in here and I look at one object, this is retrieving that object That's the same thing as this or getting that objects. And then I already just showed you the create and delete stuff. So what we need to do is make views for each aspect of crowds so define. And then I'm gonna call this the Blawg Post list view, and again it's gonna take in the request, and then it will return a response. I'm gonna go ahead and paste out these for each level of crowd pay. So I've got a list for you, and then we have a retrieve view. Then we're gonna have an update view a creative you and a delete view. Okay. So I could put it in the order of cried. Just so you see, Well, this view is a version of the retrieve you, so it's gonna have a specific method for them. But this is what we want to work towards. We want to actually have all of these different views to actually handle the data in that crowd way. And this is common across all weather locations. I will say that sometimes these views conduce do all of these things. Sometimes they do only one of them. We're gonna focus on just doing one of them and will go by step by step. 28. 28 CRUD View Outline: we have five different crowd like views, so the list view can list out objects, right? This could also be a search for you. We're not gonna do that. But the idea here is that we're gonna list out some sort of objects, and we're gonna return those objects as its specific your l. So naturally, our template name is going to be the blawg post list dot html The context. Well, it's gonna have to be some sort of list. I'm gonna use the convention of object list, and I'll leave it empty for now. Okay? Because I want this to actually return a list of items. That's it. The next one is the creative You. Right? So what do we want to do here? Let's go ahead and use very similar syntax this time. We'll just say, creates done HD mail. This one. I actually want to be able to create objects. But how? What? We would use a form so we don't have forms yet, and we don't know how to use forms yet, so we're not gonna do that just yet, But the context variable itself would be the form like, we'll wanna have some sort of form in here, and that's gonna be an object. So I'll just leave it as a string or an empty string for now. Or you could even say none. Okay, the next to the block post to retrieve you. Well, very similar to the creative you, of course, the template name is different. Post retrieve. And this is gonna be one object or the detail view that's actually a better term for it. It's actually why we were calling the block Post detail page. Detailed view is often what you would call a retrieve you, because again, the list for you and the detailed view are both retrieving data in the sense of crowd. But one is for one single object. The other one is for several objects. So we're going to call this the post detail view now. We already have how to do this. We already did it so I can just adjust things slightly here and bring it into that view as well. Okay, so that's already done. Great. The next is the update and delete view. So updating delete will have to grab that original object so they're actually based off of that original object for both of them, right? So I want to update an object. I want to change something in it. And then I also might want to delete something in it. Okay, So what happens in the update view is similar to the creative you accept. We actually pass an object. So yet again, I'm gonna add a form in here as some of my context. All right, so, of course I don't actually know what all of these things they're gonna do exactly yet or we don't. But I do know that I now have a structure to work towards. And this is true across all kinds of APS in weather education in general. It's also true about in Django, right? So in Django itself, we're gonna be working through these views to ensure that our blawg is working in the way that we wanted to. Now you don't have to do all this because so much of crowd is handled by the admin itself right now, which is a good way to actually check against what we're doing. In other words, to check against our views and how they're handling this data. That's also pretty cool 29. 29 Blog Post List View: Now that we understand where we want to go, it's time to talk about Mawr advanced querying of the database with Django. In other words, it's time to look at our model's on another scale. The first thing that we would need to do in our blood post list is actually send in a query set of some kind, which Q s is often the variable that's attributed to this. And it's literally block post dot objects that all so dot objects is a Django manager. That allows me to call dot all so I can literally get all of the objects in the database, and I don't have to modify anything. I don't have anything to the admin or the models or anything like that. It's literally just this one call, and that will allow me to have some sort of query said, Come back. That is really just a list of objects. So it's a list of python objects that we can use dot notation to see what's inside of that crew set. So that's a list view that will literally list it all out. Okay, cool. So let's go ahead and finish off this list view. Let's actually look at it with a template Going to go in my templates and gonna make it new folder in here are actually I'm gonna go to my templates. I'm gonna get new file in here. And I called it blawg Post list that HTML Okay, post list. And just like our detail one. I could copy all this and paste it in here, but this time it's coming back with a list instead of, you know, the actual objects. So we would iterated through so four object in object list, and then we would display all that. Now, of course, in a list view, you probably don't wanna have it looked like a detailed view, right? So this is actually rendering out all of the details currently. So instead, what I'm gonna do is just put it into a list element and use the object on title, and that's it. Okay, so now that we have a list and the view associated to it, I'm gonna go and copy this and import it into my your else. So from these views, import it in. And now I can bring back that original blonde path and put vet as my view, which I think makes the most sense, right? So if somebody got rid of the slug or the detail, look up your l. It would go to the list. Let's go ahead and check it out. Get rid of that. And what do you know? It's a list of all of my block posts. Now I will, saying This is getting a little bit more advanced, but I will say that you can filter this down. You can use that dot filter notation to have it be based off of something like Title I contains. Let's say hello, all right, so this would filter down down to probably one so refreshing here and now it only shows me one object, right? So that's where the could be Search comes in. But we'll leave it in as dot all because that's more realistic to how we're building our listsview 30. 30 Routing the Views : Now that we've finished our list for you, let's go to our Blawg. Creative you. This takes a form as context. So I can't really go into this just yet, But I will make my template at least, and I'm to make it based off of the block post detail. So copy that, and I'll go ahead and say blawg post create got html and we'll pace that in there and we'll just say, create you block post se events and we bring that view the block post creative you. We bring that into our your els and I'm import things in alphabetical order. And now what I can do is I can make another in point for blawg create and we would say something like that now the reason I actually I'm not gonna go. This method is because slugs and create end up causing potential issues. So what I'm gonna do instead then, is just do blawg dash new. So this is a completely different endpoints. All go ahead and put it below and there we go. Okay, So they now to just verify that that's in there. Well, just go to Blawg Dash New And now we have that template, actually rendering right. Okay. So, yeah, we definitely have to come back to that and do a lot of things there. We have our block posts. Detailed view. If we go into our your l's, we're just going to change our block Post detail page to the detailed view there really not any difference. But now we have our actual blawg items. Looked like I had a import error. I just want to make sure that the detail page is no longer there. Okay, so we go back to our blawg and then something like, Hello, world. All right, so it's still rendering that views get. That also means that I can probably get rid of this. Okay, Next is the update view. Same thing as a preview. I'm gonna just go in and copy this. Make a new file. Blawg post update dot html In this case is not actually different than the detail view. We can leave it as the same. So I'll bring in that detail view stuff. And while I do that, I'm gonna do the same thing for the blawg post. Delete. Okay. The only difference is I'll just append something that's related to it, as in delete and athletes. And then finally, I'll go ahead and import those two views into my your l's update view and leave you come back up here. Let's go ahead and just copy and paste a couple times, change these updates and delete. So to me, the logical pans or your l's for that would be something like slash edit and then flash leaked. Okay, so we update it and put the different views where they need to go. Okay, so we now have the views mounted and set up. We have the Urals routed correctly. It's time to do one more thing related to your l's before we jump into the forms. 31. 31 Include URLs: you remember back to the settings. When we went to the installed APS, I said, Each one of these APs should do one thing and one thing really, really well. Another idea is that they should be mostly plug Herbal. What I mean by that is grabbing this blocker code here the actual app and dragging and dropping it into a another project that need that same kind of app. I don't want to repeat myself at all, but if I have to repeat myself, I want to do it as little as possible. And that's where the plug Herbal comes in. Right now, it is not plug herbal, and it starts with the Urals. So the Urals right here are just simply not that great. They are repeating at least one thing right here, and you also might imagine if you had, let's say, 10 different APS. Your Your L Page is going to start to get fairly bloated. So we already have an example of something that works really well that we can also emulate . And that's the admin Urals, right? So the Emanuel's doesn't have all of this bloat because the Amon is plug herbal and it's has a euros module inside of it. So we come into our blawg and we make a new euros module as just straight up your Els. We can literally copy our main site, your l's, and bring them in here. Pace him in. But of course, we only want the Urals and the comments that are related to the block. Right? So to the relative you. So I'm gonna go ahead and do dot views, import all those things, get rid of those other views. I also am Onley gonna have the path in here. I'll get rid of the Django admin stuff because I don't need that. And then I'll just go one by one and get rid of the things that I don't want, which is anything not related to the block? Cool. Now that I have this, what I want to think about is am I your Els? Do I get rid of these things now? Of course you don't need them anymore. Actually, the only one I believe is this creative. You. We'll get rid of all of the other crowd related items and also get rid of the imports for them. Okay, but I still want my passes to be the same. And we actually already have a example inside of Jang Go's docks as it ISS. So all you need to do now is actually import the include block here and then again setting a path. And there's gonna be blawg with a slash at the end. And this is going to include the blogger dot your l's. So what this does is then looks into the block gap into the Urals module and it looks for the euro patterns like this, but notice how they all start with blogged or this right here. So for flog and these all start with blawg, I don't need those twice. I only need it one time so I can actually get rid of all these and also get rid of the create one. Okay, so you say that and we look back into our your l's here and every Oh, so we've got our blawg here and I could refresh it Looks like things are still working, right? So the path itself is coming here and path is coming here. So if I actually kept, let's say, for instance, I kept blogging on all of these will just simulate it right now, but the server restart. And now it's Saint Blawg. Blawg like I literally would have to have Blawg in it twice, which is valid. It's just kind of weird. You can certainly do this. It just doesn't really make sense for what we're doing. So go ahead and get rid of that. And that is a much more effective way. Toe, actually, Have our Urals come in now? Why is it that I have this creative you separate from these Urals? It everything has to do with the slug itself, Right? So if you actually had a slug for your blawg and that slug was new or create or something that would actually reference this creative you you would have a hard time diagnosing where it's coming from because you wouldn't be able to see that view. What I mean by that is let's go ahead and take a ah, look at this for by calling it hello world and going into that block post itself refreshing in here, it says create a new blood post is not actually going to that hello world Post. So the same would be true if we used create for new. It would go off of that view first and then every other view that comes after it, which again is why I'm doing Blawg Dash new right now. That way, the actual look up is going toe where it needs to go. So this should inform you. The last important part about thes your Els is that it's reading them from top to bottom. So whatever it matches with first, that's what it's going to stop at and then render the related views for that. And in the case of the block dot your l's or this include call, it will go through all of those Urals first, and then I'll come back and do all of these. It's pretty cool. 32. 32 In App Templates: There is certainly a another element that we have to consider when it comes to making this more plausible. And it has everything to do with our templates or, more specifically, are Blawg templates. So let's go ahead and go to our detail, and I'm just going to change the name of the detail real quick to something that doesn't exist. So blawg underscore post on the store detail, too. We say that we go to a detailed view. Refresh. You see this error. So if you look at what's going on here is, it's trying to load the template by looking into the file system loader into our templates directory. It's looking for post detail to done aged amount. What it's also trying to dio is it's trying to look into the app directories. So right now, the Onley Templates directory in the APP are related to the admin app and the off app, right? So, in other words, it's looking for template directories in all of these APS, including our blah Gap, and all it found was template directories. And here and then it looked specifically for that file name, which, of course, doesn't actually exist here, so What that means is I can actually come into my app and make a new full during here called templates. So this is the app directory level of templates. We have our file system or our custom templates right here, which are denoted by that file system loader They did not have right here. No surprise there. So if we actually add in a templates folder to our blawg, we can put them in here. So let's go ahead and solve that problem, which I just called believe was block post detail to dot html. We saved that. And now for a refreshing here, I should Let's restart our server here and now I get an empty page. Right, So this actual template here doesn't have anything. Okay, so I can easily move all those templates over. So let's go ahead and reveal all these in Finder, and I'm gonna move them over to my blogging app now. Okay, so here they are, was closed, amount inside of our project. And let's go ahead and grab a love them that are related to the block post, and we'll bring them into that templates directory now. Okay, so they're all moved and I'm gonna rename this back to just being one, and I'll delete that to hear, so I gotta delete that file. Okay, great. Restart the server again. Because we now have moved all of those. And what do you know? It loads them up, and we go into the blawg and we can check every single one if we need. Okay, so this is close. But we actually want to go one step further. And that is by creating a new folder in here with the same name of the AMP. And put them all in there. In fact, we can also renamed them in here is well to what they are. So creates the leads detail list and finally update. Okay, so now we've got these items in here. I'm going to change the template names for each one, and it's really simple. We just put a slash just like that for each level, and then we re run our server again. Just close it out, rerun it, go in here. And what do you know and reloads? It works just fine. So now this app I could send you this whole lap and it would be exactly the same. All you have to do is put it into the settings right here and then add in these two Urals. That's what a lot of third party APS do, right? They don't actually put it into the file system folder here. But the nice thing about doing it this way is that if you did send me your applicable app like this and I wanted to make a custom change, I could just come into these templates and do blawg and do something like detail dot html and literally change it right here. So these templates will actually override what you have inside of any of your APS. So that's another cool feature of them. Of course, I'm not gonna do that. I'll delete that folder. We don't need it. But now we have just a little bit cleaner of away and, um, or plug away for app itself prior to jumping in to doing the forms 33. 33 Submit Raw HTML Form: before we jump into handling forms in our app, we're gonna go ahead and do it in our contact page so that contact page view making sure that we still have it wrapped in and it's still there. All right, so I'm gonna change the template from hello world to form dot html. So that means my templates gonna make a new one called form dot html and this is going to extend, so extends from base dot html and it's gonna take in some block content. Okay, so we're gonna go ahead and put a form in, and this is just a raw HTML form. So the first thing you can do is input type, and you can say something like text and give it a name in my case will say full name. You can also do input type email and give it an email. And then you can do text area also with the name and just say content. And then you can also do a button type eagles to submit, and we'll just say send. Okay, So now let's make sure our view has formed an HTML, and it also has this context. So will add that into our form. And I'll just say, If title while the H one title, the reason I did this is because I have this h one tag here. If you just did raw title, that h one tag would still be there. It just would have something empty. So I just did it like that. All right, so now let's go to our contact page. I believe it's just slashed contact, and we have contact us twice. And the reason for that is our based on HTML. We had this right here, So let's go ahead and get rid of that. And we'll just be working off of this block content. Okay, So now I can fill up this form, however, like right? So my name and then maybe my email and then hi there and I could hit sent. What forms do by default is what's called a get request. All right, so they put all of those parameters into the euro that is done by default. If you don't declare what kind of method it iss so back in the form, we did not declare the method, so let's go ahead and change it to being method type of post. There's another thing you can add in here called Action Action means What in points do we want to send it to? In our case, we're sending it to contact. However, we don't actually have to put that. If the Vue that's rendering that form, like in our case is this view, we can actually just put a period there or leave it empty altogether, and it will submit to the same view that is handling it. Of course, if you wanted to have this on a different view, you could do that as well. So go ahead and try out action after you understand what we're about it. So now if I refresh in here, let's go ahead and go back into contact just raw contact there. Put my name in, put an email and some message and hit Send. Now I get this CSRS token verification. This is built in security by Django. All we need to do here a CS r f underscore token, and that will render the necessary field to allow me to submit this data. So going back in and try again this time I had sent no errors happen. The form is cleared out. Um and well, it seemed like something happened, but the data went away. So what actually happened? Well, our view actually did handle this request, but we didn't see any of that data. So what we can do is we can print out the request data that's coming through. In my case, it's request post its post data, so that would be a way to do it. Now, if I just go to this page and look at that print statement, I am not sending post data. I'm using the get method. So this isn't just a empty dictionary, but if I actually submitted again now, what I'll see is all of that data. So we've got our CSR f token. This is a security token. So no other web page could just submit this data, and then we have all of our fields filled out. That's pretty cool. So that means that our data is actually rendering in a raw form. We do not want to use the raw form data. So instead, what we want to dio is have Django forms handle it for us. 34. 34 A Django Form: So now what we can do is create ah form for this contact page. So I'm gonna go ahead and put it inside of this configuration directory, and I'm gonna call it forms that pie. Typically, you would have your views and forms and nap, right? I'm doing this as an illustration purposes, not something you would want to use long term, because what we're actually building up towards is really just making our app reusable. So we're going to go ahead and come in here and do from Django. We're gonna import forms. I'm gonna make a new form class, and this is gonna just be our contact form inherits from forms that form. And now we wanna have a few fields. So the fields in here, we wanna have our full name. We want to have our email and we want to have some content. Okay, so the full name is gonna be equal to form Stott Char field or character field. The email itself will be forms dot email field or an email field. Right. And then finally, content is also forms dot char field. Okay. And we can pass in a different widget and say forms dot text area. We'll see what that looks like in a second. Okay, so with this, I'm gonna go into my view, bring it in here so we'll do from dot forms imports that form. And now whatever do you is saying the form is equal to that. And then we'll actually pass in this post data or none. As in, If this post eight is empty, it's not really gonna do this next step, which is validating the form, and we'll just say, if formed aunt is valid, go ahead and print out form dot Cleaned data. No worries will go over this stuff in just a moment. So you say that Give her that posted data now. And let's look back at our raw form here, I might have to restart the server again. And I've got this attributes area. I think I have ah, spelling or capitalization problem. This should be a lower case. A That of course, could be looked up on the Geno docks and let's go ahead and we run that server. Okay, so now, um, I don't see anything that's related to Django here. Still still that raw form, right? So it's still formed at a she male here. Okay, so I can submit it again and had sent Look at my terminal. And what do you know? I now have a dictionary that's actually related to Django. So what I actually did was I sent that request, the data that request postdate up, and I validated it. I don't know how it validated, but I know that it did right, because of this block here. So again, I don't know how it validated, so I will have to check out validated. And the way I do that is by using the general form instead of our raw form. So I'm actually gonna pass in that form instance to the temple itself, update how I write out my context here. We're gonna pass this like that and then in my form itself, instead of having these three inputs here, I can render out that form and just do formed out as P. So the forms being passed as context as we see here this is deformed class itself. That form, of course, is coming from a Django form. And then in our templates, we actually render it like this. Okay, So refreshing. Here and the form has changed. I now have labels for everything, and if I had send, it's still sending that data. But it's also not refreshing that form. It's keeping that data there. So if I were to want to refresh it, I could just reiterate that former re initialize that for mother, and then this is how I'd re initialize it. So I resubmit it. And now it's a clean form. And now it's all coming from Jingo. So if I actually submitted a bad email, let's say, for instance, like ABC at ABC, no extension and I had sent Well, first of all, it's gonna make me fill out the fields that are required, and now I had sent. This is jangle form validation right off the bat. So right there, it's cleaning and making sure that I have really good data. So I'm actually not gonna go any further on this concept. As faras theory, raw general forms are concerned. There's a lot of things in the documentation to do that I will talk about validation mawr later, but now I think you're at a point where you're really close to being able to take this data and storing it into a model itself into its own app and doing those sorts of things 35. 35 Saving Data from a Django Form: Now, what we want to do is use the same concepts, but for creating new blood posts. Right? So new model objects. We want to store that data in the database. Now, what we're about to do you you could absolutely use for this. But you would probably in the case of contact form, you'd probably have a different model specifically for that. So let's go into our block, and we're going to save this file as forms DuPuy And we're going to do from Django import forms and give it a class. This is gonna be my block post form, and it takes forms dot form. Okay, so the first thing that I want is the title, right? So this is forms dot char field or character field. Then I'm gonna want slug, and this is formed Stott slug field and then finally content right forms dot char field. And in this case, we could do forms that, or rather widget, equals two forms dot text area. Okay, so now that we've got this, let's go ahead and bring this form into our creative. You This one right here, So I'll go ahead and import it in Will do from dot forms import the block post form. And then, naturally, in our crave you we can say form equals two post block form. And again, it's request that post or none. And then if form that is valid, then we'll go ahead and print out the validated data or cleaned data. Cool. So naturally, I'm gonna have this form in this context. And now we can actually think about what template I really need. I realize I made a template for the create, but perhaps I can use a different template, right? Perhaps in this create, I say form Daddy, she male. And the reason for that is because we're just rendering out of form on this. It doesn't have to be, like, specific to that view. We can reuse this template over and over again, which is kind of cool. So naturally I can actually extend from that template itself. But that's sort of redundant information. Instead, what I should do is, in my view, just change the template I'm using and say form dot html. Now, if I was gonna make this absolutely plug Herbal, I would also make a form dot html here that copies this other one. All right. So again, if I could drag and drop this somewhere, I would certainly want to make sure I have that. Okay, Cool. So I'm gonna be using the other one. So the templates, the original one. I'm not gonna be using this. This one that we just created. So now we've got this. I've got formally she male. I can actually take a look at this view and let's make sure our servers running, and then we're gonna go ahead and do blawg new. And what do you know? I have my foreman here, and I get type out whatever I wanted and had sent. And when I hit send, I see that content come through. So this is the actual dictionary, so I can save this data inside of my view. All right, so this is dictionary data, which means that I can grab each key value pair and say something like title equals to form dot clean data and then the title. Right. And this would be really useful if you had a form for multiple models, right? That's certainly possible. Right? So you might have 15 different fields in here and you want them to go to three different models. For example. This would be a way to do that. You actually grabbed the individual data and then you would do something like object equals two blawg post that objects that creates title equals the title. I realize you may have not seen this in tax yet. It's not a whole lot different than doing this. And then o b j dot title equals the title. Ah, but this is the built in Django method versus the python method where you would then do Obi Jadot safe. All right, so this is just saves a little bit of time setting everything up. Okay, So, of course, if you have another model than you do object to equals two other model that objects that create and then whatever you need to put here, So that's pretty cool. Of course I'm not worried about that right now, but instead what? Aiken Dio, instead of actually getting each value from this data, I can just pass in that dictionary and unpack it with two stars formed up clean data. What that does is it takes this dictionary and turns the key value pairs into arguments much like we just saw, which is pretty cool. Okay, so now I can get rid of that. And after I save that data or create that data, I'll just do form equals to block post form, and I'll just re initialize that form. Okay, Cool. So we saved that. And let's try. Now, I'm gonna actually send the same data that I had. I'll send it a couple times with a different slug, and we'll look in our admin for the block posts. And what do you know? They're actually in there. 36. 36 Model Form: Now, this is pretty cool that I can do that now. I can save data from this form, but Django has something built in four forms. And for models, they're called model forms. So basically, what I mean is it's going to take the block Post model. So called this block post model form and its forms dot model form. And I can actually use the model itself. So we stayed class meta model equals two. Of course, we need to import that model. So from the models import blogged, post model, or rather, just brought post, I think that's the name of our model. And we set our model down here in the middle class, and then we do fields equals two title slug and content. Okay, so now I'm gonna go ahead and use this in my view, come in here and change our blogged post form into our blood post model form. And now what I'm gonna dio is just you form dot save I no longer need that create. I can just do form dot save just like that. And again, I can re initialize. So we say that Let's go back into that form. This time I'm gonna go ahead and make another new one who are title has changed. Well, explain why in a moment, another title, another slug and some content we had saved. We look in our block post and men and we refreshing there. And of course, there's our actual blind trust. Okay, cool. So a couple things happened. One of the things was this title. Why is it a big text area? Well, that has to do with our model, right? We actually created the title as text field. Well, I probably don't want to have my titles Being Text field Textual allows you to have a lot of data in there, right? So that makes a lot of sense for my content area, but not my title. My title should be char field or character field here, and we have to actually add in something called Max length, equaling two like 1 20 or 120 characters total. And then I'll go ahead and save that. Now, another way to accomplish this is to actually override the fields for each field that I'm bringing in, right? So I can actually use this same thing here, and this will actually convert it into a char field, too. Now you can still use widgets, so you could keep it the same as it. Waas like this. But really, this is probably the method I would end up going if I really just need to change the form and not the model. But I want to change the model because I don't need that. And another important part to note is that I just did form, not save. You can't do that unless it's a model. So I can also manipulate some that data by saying object equals to form, not save commit equals two false. That means that it's not actually saving it. And then I could say object. A title equals to form dot cleaned data don't get the actual title. So this is a dictionary, so we can call dot get on it and then just add something like zero or something like that, Adam and then object up safe, right? So then allows me to manipulate some of the data inside of the form itself. So let's go ahead and do a couple things. Here we have. We made changes, the models, So we do. Python managed not. Why make migrations? And then Python managed up here. Why migrates? And then we'll run the server again and we'll test this part out. We take a look at our block post. Now, now it's down to a field that makes sense will make some random slug and notice that it ends with an s as the title I hit send, I go into my block post Tear ate it added a zero to the end because I added that zero strength. Of course, I don't actually want to do that, but it's nice to know that I can make some changes from the model form just like that. So that's model forms. They are pretty cool there, based off of the model itself. And then you would just declare what fields from that model you want to include. 37. 37 Validate Data on Fields: if you remember bank to our contact form, and I used something like ABC at ABC with some whatever. Other information it said enter a valid email interests that is called form validation. Now we can have our own custom form validation as well. So let's look at the email itself. Right. So what I want to do is I'm gonna make my own custom form validation. So I do clean underscore. And then whatever I named the field, right? So these are arbitrary names. They do make sense for what it is. But you would say something like email, and it takes in the argument of self and you can do arts and keyword arcs. So to get the actual email, you would do email equals to self dot cleaned data dot get email or the name of the actual field. Right. So this gives me that value, and then I would return what that value is. Okay, so let's take a look. I'll go ahead and print out that email, and this time I'm just gonna add, you know dot edu, You okay? We had send in my terminal. I see that I printed out two things right ones from the view once from the form itself. So it actually printed out that email just fine. So I can prevent certain kinds of emails from coming in here. And this is true for any field, right? You could You could run a simple method to check whether or not this is valid email so I can do a condition. And I'll just say if email dot ends with the EU So if the email string ends with dot edu, then we'll raise a form stop validation error and say, this is not a valid email. Please don't use dot edu. Some like that. Okay, so I refresh ice resubmitted. Same exact one with that email. This is not a valid email. Please don't use it. You cool. So that's a way to do validation right in our form. Now, naturally, I can also do this same thing in our model. Right? So let's go ahead and do title. So this time I'm gonna say clean title title is one of my fields. This time it's actually coming from the model itself, not the form itself. So again, I have to change everything from email to title and something that I can do is actually run a query set on this So I can say Qs equals to block post, not objects that filter. And we would say title equals two title. So this is looking to see if there's any values of that. And then I would do something like Qs thought exists. This the validation error being this title has already been used. Please try again. So now I have a way to actually clean out what that title is and let's go ahead and make a new one with the title that I know exists of blogged ash New. I called it Tesla, one of them waas, I said ABC, And then I hit some content. And what do you know? That's a very practical example of a title that we don't want to reuse without putting that unique clause in here, right? So the form will also make sure that we aren't using that unique laws. So let's go ahead and grab one of our slugs. Doesn't really matter which one and had sent that will also show up, so that's pretty cool. Now what I actually showed you was another way to prevent the same exact value from being used again. And to really make this crease I call better. We used the case insensitive. I contains, or I exact. So I exact means that if I used a lower case t for Tesla and hit send, it would still say the same thing. Now if I didn't do that and I ran it, it doesn't say that thing right. I still have a validation error with slug, but I have to capitalize the t in order for that to work. And that's why we use I exact here. In that way, it doesn't matter how things air capitalized in here. It's still gonna be actually going off of that pretty cool. 38. 38 Login Required: financially. There are two things that we want to have happen when we create data. One of the things is we want to prevent who can actually do it. Like right now, Anyone who has access to that, your l could actually make a new block post. So the first thing that I want to do for that is used the log in required decorator. So do from django dot con Trib Got off. Got decorators. We're gonna import the log in required decorator. Now, with this, I can just go ahead and say, Aunt Log in required. And this is a rapper that goes around this view and checks whether or not this user has a session and they are a valid session, so they're actually logged in. So we come into our block new. In my case, it works because I'm still logged in to the admin site. Right? If I go into an incognito window, as in I'm not logged in, it does not work. It actually prompts me too long in now. I can change where I log in, right? So I can come in here and say, log in you or l equals two slash log in and try that again. Me and I get an error. That's restart the server. Refresh. Um, and we're gonna block new and there goes, so it actually changes that your L Now, I can also change this in my settings. We simply by going into settings and saying log in your l and changing wherever that is. And then, in my view, I don't have to do that argument anymore. So we take a look. Copy that, pace it in. And sure enough, it changes where it needs scope. Okay, well, that's cool. Um, there is another decorator that we will talk about and that has to do with whether or not the user that's trying to access. This is Steph. The other one is called Staff Member required. And you can actually use to decorators together if you want. In my case, I'm just gonna have one. Okay, So go ahead and import the staff member. Require decorator from a slightly different place, and it's from django dot con Trib. But admin this time, Doug views decorators, imports staff member required. Okay. And now that we've got that we look in to our admin toe, any specific user. It's rerun the server, and we see that there's this permissions aspect where we can do Steph status. And that just means that they have to have staff status to actually get to that page and have to go to Block knew it automatically forces you to log in to the Django admin, which is pretty cool. So we now have two ways to protect any view. We can do it for any logged in user or a user that has permission to do these things. Now, if you have, like a bunch of blawg writers on your staff, then yes, staff member required might work. You could go into doing mawr additional customization, but at this point, these are both really good for what we need now. So one other thing that we need to talk about is actually associating any given blood post to a specific user 39. 39 Associate Blog Post to a User with Foreign Keys: so the other thing that we do when we want to create data is actually a associate of that data to a specific user. And we do this by declaring what's called a foreign key into our blood post. What a foreign key means is that we can tie in one model to another. It joins these models together so we can do all sorts of really cool things. We'll see what that means in just a moment. Now what do I want to actually associate this block post to? Well, I want to actually associate it to a user. As in the user, that's actually adding this block post. So do models, not foreign key. And we need to actually get the model that's related to this user. In other words, we need to get the user model here, right? So to get the user model, we have to do it in this way. We do from geno dot cough import settings, and we say the user model is equal to settings that off user model. Now, for users, this is literally the only way you do it right, because if you change the the default jangle user model this would still work versus actually trying to import the model class itself. If you don't know what I mean, It's OK. Just remember, whenever you need to use user, do this. OK, so we'll go ahead and say, User, and I'm gonna go ahead and add a default in this case. And I'll say the default is my first user, which in my case, is my super user. Right? And that goes back to like the Knoll blank stuff when we actually changed the database. But now that I've made some changes to this model, I'm gonna go ahead and run. Who? We get an error it saying it's missing a positional argument onto elite. Now, this is a new thing for Django. 2.0, so older versions of Dangote inferred it did something like this. So models dot Cascade. So what that means is, if I believe this user like the actual user from the database, it would delete everything related to it, including this block post. Right? But what I want to do is set Knoll. I want to make sure that if I delete this user, everything that's related to that user is gonna be set Knoll and you'll see what that is in a second. So say no equals the true there. And now we're gonna go ahead and run our migrations. So Python managed happy. Why make migrations? And then Python managed up you. Why? My great. Okay, so we run that server again, and let's go ahead and look in the admin. Okay? So let's go ahead and create a new user, and I'll just call it author. Like, as in this is a block post author. I'll give them staff status. I gave him whatever password, and I will go ahead and add them to new block post. Okay, grab that author. Right. So they're associating this block post now, another title, Another title slug or something. And then content we had saving. Continue. Sure enough, we've made that. Now I'm gonna go ahead and delete that user that users now deleted. So this object in our database is going to be deleted. And then now when we go to our blood posts and we go to that recent one, it actually goes back to the default user, right? So in this case, the reason it went back to the default is because I set a default. Otherwise it would go to Knoll. It would be completely empty. So that's pretty cool. That's if you had sewn on your staff that did a bunch of stuff, and then you needed to remove them from Django. This would be a way to do that. And in our case, we actually set a default value. So then that user stuff would go back. Now there is one other thing that I want to show you about this, and that is by jumping into the shell. So we do. Python managed up you, Why shell? And this time I'm going to do a different kind of import for the user model. That's from django dot con Trib dot off we're gonna import the function called get user model and we'll say user equals to get user model and use those parentheses to actually get that. So this is the actual user class, right? So we don't want to import the user class from here, Ever. You want to import it if you're using it. All models, not pie for foreign key. You do it like this. If you're using it anywhere else, you do it like this. So the reason for this is I can say Let's say J equals two user The objects dot first, right? I only have one user. At this point. I deleted that other one, right? So I now have this user J How do I actually get all of Jay's block posts? Well, j has, or that user has a foreign key to block posts, so I can actually do a reverse look up. That does j dot blawg post underscore. Set that all explain that in a second. But we hit enter, and that gives me the query set that's related to this user notice. I did not import the block post itself, right that bought block post class. All I did was grab it from that users instance, and this is what foreign key is. Do they actually associate this data in a way that we can do these kind of dynamic lookups Now, the reason I knew is blocked post outset or block post underscore set Rather is because the actual foreign key is on this model. So all you need to do is lower case. The name of the model in this case was blocked post and they use Underscore Set, and that will actually give you the query set that's related to this user. I realize this might feel pretty uncomfortable, but just remember that whenever you need to get the reverse of this data, you can do it this way. I will show you one last way to actually get that data, and that's actually importing that model itself. So from blogged up models import the block post, and then the same exact query set will be blocked. Post that objects that filter user eagles to that user instance right here. Or you could do something like user underscore underscore i d equals two Jadot i d. Both of those lookups would work, and then we look at that query set. It's the exact same, so those are literally the same ways on how to do it. But of course, this one gives me, like, a lot less work to make this happen. We're going to revisit this again, but I just want to make sure you knew that that is one of the powerful things of using a foreign key instead of like, instead of saying that or author is you know some sort of author, and we put it in as a char field or character field, which is fine if you need to do it that way. But actually associated into the user does have one more benefit. 40. 40 Logged In User & Forms: Of course, there is still one of the benefit that we didn't talk about. All we showed you was how to create a block post in the admin. And I would imagine you could do it in the shell to I think you have enough skill of this point to be able to do it in the in the shell. But I'll give you a little hint. It's block post objects that create, and then you add the fields that you need. I'm gonna go ahead and exit out of that old shell and I'll run the server again. The other benefit to using a foreign key versus, like a field to actually associate the user is, too. Use it in our view. So we added these decorators here for a reason. That reason is that we know now that request that user will actually return something right because of either one of these decorators. If they weren't in here, we'll actually take a look at that, too. So with that, that means that I can come in here and say object that user equals to request that user. It's just really that simple. So now when I go to create a block post. I could come in here and create another one with some weird slug, and hopefully that works. And it does. We're going to our block post here in 10. And sure enough, it's associative. Now, maybe you're like, Wait a minute. We set that as the default anyway. So how is that possibly correct? Well, let's go ahead and test it out with a new user. So again, I'll bring back that author user will use a whatever password, and I'll give them Steph status. Right? So it had saving Continue. We're gonna open up an incognito window now into our project here, and then I'll go ahead and go to block New. This will prompt me to log in to the jingle admin. We go into our author and it brings me back into the actual blood post. And I'll say, you know, something like this, and whatever I had send, we look back into the admin from our other user. This the main super user. We see that we have a new block post, and sure enough, there is our new author. So this, of course, is that other benefit is being able to actually set to that data when you create data rights, actually set it to the request. That user. And now it's associate ID going forward. Now, if you didn't have these decorators in here, let's say, for instance, it was like this. What's actually gonna happen? Well, I'm gonna go back into the incognito window, make sure I'm lot logged in. In my case, I'm not logged in. It says Anonymous user. And now I'm gonna go in New block New, and I'll type some stuff out. Let's make sure half natural. Original post here. I hit send, Then I get this. What? So this is a valid error, and it's saying that it can't assign an anonymous user to the user field in our blood post model. That means that this right here can't be the class of anonymous user, right? So if you're not logged in your not the actual user model itself. So to solve this, there's a couple things that you dio Well, of course, you would use those decorators. Another would be, if not request that user that is authenticated. Then we would return some other template, some other data. All right, so, like, let's say not a user dot html. I don't have that. I'm not actually gonna implement that because I don't think this method is good in the long run. But now that I have that, I let's go ahead and refresh this page. Now I get a template does not exist. That's expected because we didn't make the template. But that would be a way that you could solve this problem without using these decorators. But of course I want to use these decorators, so bring it back. And now, of course, is gonna force me to log in. So go ahead and get rid of this. Just a method to show you how to handle Actually associate in a user to form data of some kind pretty cool. 41. 41 Update View with Model Form: Now, the question is, how do we use this model form to actually perform are updating, right? We have our creation. We have our detailed view. Now we need to go ahead and actually update this particular object, whatever that object might be. So to do this, we want to bring in that form again. So the exact same form, and yet again, I'm gonna go ahead and use the form dot html. And this time I'm gonna go ahead and do a title and say updates. And then whatever my object is, I used the f string substitution with Obi Jada title, right? I don't need to actually send this object in anymore because I'm using formed out hte email as my template. And then I'm gonna go ahead and pass that form in as context as well. Okay, so now, now that we've got our foreman here and we have our title, we're gonna add one more thing into the initializing of the form, and that is instance. So we looked up the object here, right? So based off of this view, So it's actually updating this object. We can pass into our model form an instance, and what that will allow us to do. Let's go back into any given block post here. Let's take a look. Got all of our different block posts. Certainly gonna have the length ease soon to let's go to the hello, World one. Okay, so get a hello world. And then I said at it. Okay. Okay. So a couple things here is I've got a invalid view. That's because in my actual view, I didn't pass the slug. No thing that I missed when I did that, but that's okay. We're fixing it now and a refreshing here. And we've got all of our stuff. We've got our title, are slug and so on. If I send this now, title has already been used. Please try again. Hey, wait a minute. This is the one that's using it. So of course that's related to our form validation stuff that this right here. Okay, so let's go and comment that out just for a moment will fix it for real, and I'll do it again. Save it cool. Looks like it's working this time around, right? So we look back in our blood posts into the title. Let's just change the content so we can see that the values changed. Value has changed. We say Say send refreshing. Here the value has not changed. Well, why is that? Of course we didn't do anything with that data. So we'll say if form done is valid, then we just do form, not safe. We run it again, send it again due to minute, 1,000,000 times if you want to refresh. And sure enough, it's eight. So I just had that update view work. That's all we had to dio That's it. Just like that. So passing this instance allow that to happen. But of course, we still saw that problem of this right here. All right, so we need to solve this problem. We're going to make sure that we can still validate data if were updating something based off of the same form, does it bring that bank? I might want that in other places, So I certainly needed here 42. 42 Better Validation on Update Views: now, often times when you want to diagnose something like this problem. What I end up doing is printing out the directory of self. So whatever is coming through in self, I want to see what that is before I do any Googling. So I hit send and it come in here and I have all of these things that are associate ID to self or that specific instance, right? So what are the things that I'm looking for? Well, what I actually, I'm looking for has to relate to the instance itself. I want to actually see if this field or this form has any relation to the instance, right? So the self is the actual form. It's the instance of that form, right? So in other words, it's this and this in this. So in any method there, I want to see what's inside that method, and I want to specifically have the instance. And sure enough, there it ISS and so you go through them. There's the instance right there. So let's go and say instance equals to self dot instance, and we'll just print out what that instances Now, before we go any further, I want to make sure that any other view that's using this form doesn't have any errors with me doing even this. So let's go to our other view to go ahead and leave that open and we'll go to the blawg dash. New. Okay, so all created in the title with some weird content in it, you know, it's end. I get none. Okay, cool. So the instance is either gonna be none or the actual object itself? Hopefully not a huge surprise there. But that means that this value then I can actually use inside of my query set so I can say , if instance, is not none. Then our query said, equals to the crew sent dot exclude. Peaking equals two instant stop PK. That's the same thing of saying I d is equal to instance that I d because those are the same values. But it's cool to see that we could do that. So what this does is it takes our query sent that matches this title. It could be a bunch of things, and then it removes the actual instance itself because we don't want to do this same validation on the instance itself. Right? So this could be true for Title. It could be true for slug. It could be true for content. It doesn't really matter what the clear set is. What matters here is that if there is an instance, we don't want that validation error on the instance that we're changing. That's kind of the point. OK, so now that we've got that, let's go ahead and test it out in both places. So I'm gonna make a new title here and say another new title, that one might be taken. So I'll just say Dash, whatever. And then we add that content that seems to work fine. And then again, I'll do the one where for sure already have one, which is Tesla. And sure enough, that works fine. So it's now validating just fine on empty data, and now we go back in to our actual post that we're updating and I keep. This is my title and hit Send. No longer do I have that error, and I'll just say another value has changed. For this data I had send and refreshing the admin. Another value has changed. Cool. So that is just a little bit more advanced validation for any given form to ensure that we can do something like this 43. 43 Delete and Confirm: now we just need to delete Are objects. For some reason, I have this syntax thing going on here. Not sure why. We'll just get rid of that comma and replace the order that seems to solve that Centex error, even though it wasn't a syntax error. Whatever. Okay, so now that we've got this, we need to update our delete view. So what I'm actually gonna do in here is just literally say, if request dot method equals to post, then we'll go ahead and do object at elite. It's really just that simple. And something I missed on the update view we also wanna have the staff member required on here for both of them, right? So the reason I'm doing this request, that method has to do with what I want this delete toe look like so inside of delete dot html. I'm gonna go ahead and say, um leave this titles. I do. Are you sure you want to delete and then have that h one in there? Perhaps we bring this down to paragraph. Sure you wanted to leave? This are the following. Then we just put in a form with the method of post and we don't need to put the action close off that form. We put the see SRF token in here. The form is actually empty. Otherwise. So we just do button a lot of class and will say Bt end between and and danger and we'll say yes, delete. So it's basically an empty form. Sure, I have that token because I need that and this basically empty form. What it does is it will actually call this method right here, and then we'll delete it. So this is really just a confirmed page, right? There's other ways to actually go about deleting these things. But this is nice, because then it's saying, Hey, are you sure you want to do this? Yes. I want to do this. Boom. We go into it. So after that happens, we actually want to redirect them somewhere else because this view is no longer going to be valid. Right? But we'll do that in a second. Let's go ahead and delete one of these and I go into Let's just do hello world and into delete. Now I see. Are you sure you want to leave the following? This is my title. Yes, delete. Okay, so now I think it might may have been deleted. Let's go and refresh the page. And sure enough, I get a page not found. So that's why I want to redirect here, and we'll just return. And if I scroll up to my shortcuts, I can import something called Redirect. And so after this myth, it happens. We return the redirect, and we take it home or toe my blood posts. Either one. Okay, so let's try that again. Got my block posting here, and Oh, shoot. I don't actually know how to get any of these, so I have to I have to go. And to my admin, grabbed the actual slug itself. All right, then leads. And then yes, Deletes. And sure enough, that blood was gone. And we look into the admin again. And sure enough, we only have a few or we have a few less cool. So now we have our to leave you. Now. I did bring up One thing is we have very, very poor navigation. In fact, we don't really have navigation. Let's go and do that 44. 44 Blog Post Navigation: we now need to update our list view to have links. Three simple. We go into a list out html and we used the A tank a traf blawg, and then object that that slug because, remember, that's our look up. And that's also what we put in our your l. So we put that there, and then we close off a tag. Okay, so that's one way to do it. We click on here, and sure enough, it goes to where it needs to go. A more conventional method would be in our actual model is to make something called get absolute. Your l and this will just return. Whatever that path isso my cases blawg and we'll use f string substitution and just yourself dot slug. So if that slug, of course, has referred to that instance itself and then in our list view instead of that slug we would use get absolutely you're out for the entire thing. Actually, we don't need slug. We put it right here. So this is certainly a Django convention. So we want to stick with those conventions and we can actually use the same concept for our other items, which waas you know, get at it. You're ill. And then this was just flush. Edit. You could call, get update. This is not convention, right? This is okay. So now that we've got that, um, I can also go into my detail view now and say if request that user is authenticated and if and then if request that user, what is staff? And if then we will go ahead and saying a treff objects that gets at it your l and just literally edit. And heck, we might want toe also just say delete as well. In this case, we can do another one for delete and leapt. Of course, you might see some redundant information here, so yes, of course, we would actually come in here and do something a little bit more like self, but get absolutely your l make sure that if there's a slash here, there's not one there. But in my case, I don't have one. Never go. And this is delete. So now our detail has the ability to be edit and delete if their staff So we look here and at it delete. Great. If I open this up an incognito window, I don't see those things cool. So that adds some navigation to this 45. 45 Include the Navbar: go ahead and add in a knave bar for this. I'm going to get boots trump dot com into documentation into components down to nab our and we're gonna go ahead and use their default here. I'll just copy it, and I'm gonna go into my templates, make a new file in here and call it nab our That makes him out. I'll paste all that code in. And now what we can dio is a another item that you may have not seen yet. And that is include, knave bar that HTML So what this does this block allows you to just bring in another HTML document so you can reuse it wherever you need. Of course, In the case of in Anbar, that absolutely makes sense. So I refresh in here and now I've got the snack bar. You know, this also may make sense for Let's say, are optional JavaScript. We make another new template and we call it Js dot html and we paste that stuff in and we go back into base and we say include Js dot html. Okay. And perhaps you do it with your head documents as well. I'm gonna leave it as is. And I'm also going to go ahead and get rid of replace this value. We don't We certainly don't need that anymore. Okay, so now with my NAB, our I can just make changes as I need. So nab our dot html my home page, which is linked here. And it's also linked here instead of having it as a hash. I'll just have it as a slash and I'll call this my try Django Blawg. And then home page right here is gonna be that. Okay, so I have this active class. I don't want that anymore. I will probably add it back, but he would go. So we've got our home page. You can click on that now. My home page doesn't make a whole lot of sense right now, but I also want to have a blawg, right. So my blawg and then we'll go into R slash. There we go. So now I can go to the blawg. Hopefully hoops. We actually put in the link to it. And now I can go to the blawg and I can look at any given item. Cool. So these extra things I don't want right, so this drop down, I'll give her this disabled or get rid of that, starting to look a lot more like something I would want to see. Now I have this search feature here. Perhaps I want to allow a search to actually happen. We will look at that. But before I do that, I want to style the way my blawg looks as far as the list is concerned, so it's going to try that out. 46. 46 Include with Arguments: So the nice thing about this include is I can also use it inside of a generation so I could say something like include nab our inside of this object list. We'll just go ahead and rerun the server and then check in here and what you've got we've got the NAB are showing up several times. Of course, that's not really that practical. You wouldn't want to have your nab our show up a lot. Instead, what we want to dio is have a uniform way to display listed items, right? So, in other words, I wanna have this be like this, Include, so that if I want it on my blawg post page or I wanted on my home page, I want them to look the same. I want a reusable template that I can use inside of other templates. So to do this, we're going to go into our block templates, and we're gonna go ahead and say list in line dot html. Okay, so now this include, instead of being now bar, it's gonna be blog's slash list in line dot html And inside of that, I'm gonna go ahead and cut out this list element here and paste this end. We save that I refresh there. And sure enough, it actually renders out all of this content. So the nice thing here is that I'm seeing that this object is being passed into include by default. But I can be more explicit than that by saying with something like, Maybe blawg post equals to object. Right? So now I'm taking one of the template variables, and I'm passing it in as another template variable. And then inside of that, include we can go ahead and do something like that. Let's go out and restart the server and we refresh. And sure enough, everything still working. You certainly have to include that with If you don't include with, it's gonna give you something like this, right? Okay, So we need to have within their to have our own custom variable. So these are completely custom. I could call it post that you call whatever it like. And if you want to stick with the object, you can also stick with objects. But I'm gonna go ahead and keep it in as block post. So that way I know whenever I open up list Desh in line I get something like this. Okay, so what's the real reason for this is to make one place to update any of my in line templates for this block. In other words, let's go ahead and make a div class, and we're gonna call it card and close that off. And then all do def class of car Dutch body. These, of course, are bootstrap classes, and you can check the reference for them right here and components slash card. And then I'll go ahead and do H five class equals two car dash title, and then we will do our car title. In this case, it's blawg post dot title and then I'll go ahead and do P class equals two car dash text, and I'll actually put the contents. I'll do blawg post that content here. Close off that p tag and that's where I'll leave it. Okay, so I'll get rid of this list element now, save that card and now it's refreshing here and what you know. So I have at least a little bit better of a display or at least something I could probably reuse now inside of my list element. I can also do a div class of card group, something like that. And that's going to change how those items are rendered. And naturally, this looks funny. And the reason why, of course, is how are based on HTML has been running. We've been putting the block content inside of all this. So what I'm gonna do instead is just put it inside of a container and we'll actually use container dash fluid. Okay, so this should absolutely change how this is rendered out. And sure enough, it is. So this is probably closer to what you might want in a blawg. Maybe not because of, even when I, you know, master down, it's not looking that great. So then he comes back to going back into a list in just saying, um maybe like Roe and than in the list in line, we do something like Div class equals two column column. You know, let's a medium six and Imex auto. Maybe the default would be like calling 12. Okay, cool. We refresh. And so you've got a little bit of a different look right? That's pretty cool. It's allowing us to see multiple things. As far as the column is concerned, you know, perhaps you change it to 10 and then it's Mawr centered, and then perhaps you had some margin to the bottom. All right, so you can play around a lot with these things. A sfar us how this is rendered and displayed. But that's actually not why I showed you this Not so much of the design, but also to show you this right here. And then if we wanted to reuse this somewhere, let's say, for instance, on our home page, we could do that. So let's go ahead and do from And this is gonna be blogged on models, imports, Blawg Post. And now I want to do is instead of my list context as it is, I'm gonna go ahead and say Qs equals to block post objects that all it will only go up to, like maybe five of them. So now my context, all your head and do my blawg list. This time it's not object list. Instead, it's just blawg list, and it's that query set there. I'll get rid of this condition. We don't need that anymore. And then my title. I'll go ahead and say welcome to try Django and then I jump into home dot html where I had all this stuff rendered out. I'll go ahead and do H one for my title. And then now the it aeration for my list was blawg list. I believe what we call it log list. So for a in blawg list now, we would just say include blawg list desh in line with Block Post equaling two A and closing off all that. There we go and now we go to our home page log list in line dot html. Template does not exist. I forgot the HTML. Perhaps you saw it and we refreshing. Now I've got that same stuff coming in, but now it's only five posts versus all of the post. So this makes it also very easy for me to adjust exactly how I want my in line list element to be rendered. And you might also be like, Well, why don't I use this on my detail as well? That's not a terrible idea, but we're not gonna do that at this point because I need to learn a few more things about how I would use this include template 47. 47 An Included Template for Consistent Design: jumping over to lip some dot com. I'm just gonna make some arbitrary text paragraphs. I'm gonna go ahead and say 25 we're gonna generate it. And I'm just gonna go ahead and copy all of these paragraphs, Laura, if some just makes it look like real text, even though it's all just gibberish. So I copy that, and in one of my block post, let's go into the, like, smallest number blonde post or the smallest I D number, that is, and we'll go ahead and save that data, right? So if we look into one of our posts, we now see whoa, we've got a bunch of data in here. So there's a couple things that we need to fix with our template. One of them is saying line breaks. So if I do line breaks, it will actually reformat it to how it should write with the actual paragraph tags and all that. But, of course, on my home page, I might not want to show this entire post. I mean, maybe you do. Maybe you don't maybe on Lee on the blog's show, the entire post. So the possibility here is to jump into our list in line again, and we can actually use that arrow or that line. The pipe is what it's called and we can use something called truncate words. And we can truncated to a certain number of words. Let's say 30 right. So now when I refresh sure enough, it goes down to 30. This is just 30 words and then adds this little dot, dot, dot There, right. And if we come over here, same deal. Okay, So, yeah, of course. This is one of those reasons as to why you would use those list in lines. So since I have this now, I also want to add one more thing to this body, and that's h ref. And of course, I want to be able to actually go to the block post itself. So block post, I get absolute. You are l. And we'll just go in and save you. We go. So now I have a way to actually go and view that blood post. Cool. So I do want to make this list in line a little bit more dynamic, though, like maybe I don't always want to truncate these words. Maybe I do. Maybe I don't. I'm going to say that I don't. I probably don't always want to truncate words. So to compensate for this, what we can do is we can pass in another argument. And in this case, I'm just gonna call it truncate and I'll say True. So on the main list page as well as the home page, I'll go ahead and say truncate being true. So that means then in my list in line, I can create a variable that will do this for me or use off of that variable. Rather So we would say if truncate otherwise we will just show it out. We'll use the line breaks still coming here just like that. Cool. So what we expect to see is the home page being truncated Still. Sure enough, it is in the block page also. Great. So the next thing is, perhaps I want to add in detail being false. Okay, Yet another element here of detail being false, and we'll say, if nots detail, then we'll show the view. Okay, Now, why did he do this? Well, the truncate one, I think it's fairly obvious the detail one is because now I can actually use the same include for my detail here. Right? The block post is still that objects like we've seen truncate now is going to be false or it could just leave it out and detail is going to be true. And sure enough, we refresh on our list. That's fine. We got a view it and yes, now we have actual consistent looking pages, right? So my the reason why detail looks a little bit different than my list is because I didn't put everything inside of a row. So let's make sure we do put everything inside of a row here and sure enough, now it's gonna be a lot closer. But of course we have these items over here that we haven't really accounted for just yet. Sold. Just put those into a div class of column 12 and I'm actually going on. Lee do that if they come through, say that in the real. So now the design is very consistent, whether I'm viewing it or not, and also we have that truncated value, and then that view button goes away. If we're on that actual detail itself, it does have at it and Julie, because we made it do so pretty cool, right? This include is a nice feature, but of course not something that is required. It's really just think, starting to think about how you want to make it a lot easier for consistent feel across your entire site. 48. 48 Publish Date, Timestamp & Updated: now, Blood post isn't really complete unless we have a published eight. Right. So I'm gonna use models and will due date, time field. So the date time field can be either the date field or the date time field. That depends on whether or not you want to use the time of day. Then we can go ahead and say auto now equals the false and auto. Now, add equals two false. And then I'll leave like that. I'm gonna go ahead and use a couple other ones as well. And one of them is gonna be time stamp, and one of them's gonna be updated. Okay, so the time stamp one will be auto. Now, add being true. The updated one. Being auto now being true, this is toe illustrate what those two values do. And then our date time field. Well, I might want to allow it to be Knoll and blank. The reason being is like, perhaps I haven't set a published eight yet, so if I haven't, then this could be an empty field. All right, so I've ended three fields to my models. What do we need to do? Python managed a p y make migrations and then right off the bat it saying You're trying to add time stamped with auto nail equaling two. True. Of course, the database needs to know about that, and we need to provide a one off value I can use time zone dot Now that is fantastic. So that means that I can set the default value now updated is going to set a very similar value. I just don't need to declare it on Lee the time stamp. Right? So this is basically saying that all of my old posts have the exact same time stamp now, So I'm gonna go ahead and run. Python managed up you? Why, my great And let's go ahead into our admin, make sure the server is running. So python managed Happy. Why run server? And we see that we now have a published A. But those two other fields I do not have, right. I don't see timestamp or updated, so let's go ahead and just save this one. So this idea of three should have a different updated than perhaps all of the other ones. So let's go into a shell and take a look. I thought managed people I shall, and I'll go ahead and do from Blawg Post. Or rather from long that models import blawg post and with the Qs blogged post objects that all and the 1st 1 soak us not first got updated. There's my updated date time object. I can also look at the time stamp itself. Not a whole lot difference, but still a value. And I can't actually change these. They change automatically. So what auto now add? Does it says when you create this into the database, when you add it into the database, this field gets actually changed to that time. When you update it whenever you hit save this will change. And then, of course, the published eight is something that I would change. Not necessarily anyone else, right? Pretty cool. So now that I've got this, I can actually look at another one. So Qs thought last updated, and we'll see that the QS that last updated is the exact same as the time stamp for the other one. So basically it's set it the when we actually added it to the database, right? So when we actually added it to our migrations, that's when that said it. There's also another error, which is the's right here. I need to actually put some parentheses there, actually run that little small thing there. But now we have a published 18 time stamp. It probably makes a lot more sense to order things by these dates. So I'm gonna go ahead and do class meta and say ordering equals two. Well, if I want the most recent one first, I would do negative published eight. If I want the oldest one first, as in it was posted a long time ago. You would leave that out. So I'm gonna do published eight. Then I'm gonna do updated, and then I'm gonna do time stamp. So this ordering has to do with the query set. Right? So the order of this query set and before change, anything it has to also do with the post themselves. So let's go ahead and take a look at what the changes do. We made changes to our models, so we run python managed up. Why? And then python managed up ey migrates. That's going around the server and take a look at that ordering change. So if we look in our block post. Now we see that the third object is now first when it used to be at the bottom, right. So I'm just gonna give that example. It used to be Where is the primary key or I d Those are the same. We would refresh in here. Um, oops. Not in that order, but rather the newest one first. So negative. Negative. P. K. This is what it was by defaults, right? And by adding these other ones, it allowed this to come out in a different way. And that also means that my list view should also be different. It should also be based off of the updated and block post. Right? So let's go ahead and update this one which had saving continue. So as of now, I'm gonna refresh. And what do you know that recently Save one is now at the very top. But of course, this is a problem because none of them have been quote unquote published. So we're gonna go ahead and do that 49. 49 Model Managers and Custom QuerySets: So now that we have the published eight in here, we see that I have a lot of objects or a lot of posts that haven't actually been published . They don't have a date that was actually set for that publishing. So I need to use something that will eliminate those from even being displayed. And there's a few different ways on how you go about doing this. Number one in your views. You could modify this to be something like this. Let's go ahead and do from django dot you tills we're gonna import of the time zone and then we'll just say now equals two time zone dot now. So if I wanted to Onley show the ones that were published, I would say QS equals to block post on objects that filter publish date underscore underscore is greater than or equal to now, or whether it would be less than or equal to down. So it happened in the past, so this would be a way to filter these things down. I comment this out and I go to my blawg. Nothing's there. No surprised we haven't actually published anything. We never said that we did so this is OK, but the problem here is that it's in the actual view, so you'll have to remember to always run these things. So what we can do instead is use what's called a Django model manager some to go ahead and get rid of all these imports and all of that stuff and jump back into my models and I'll import the time zone here. So from django dot you tills import time zone and we'll go ahead and create a mall manager . So do Blawg, Post Manager. And this is models, that manager and they will just do define published. This is arbitrary here, So published takes itself and is gonna return self that get query set this part's not opportunity and then filter well, that same thing. So we say now equals two times owned up now and then we're gonna filter are publish date less than or equal to now. So this is this get query set thing. What that's doing is blawg post that objects. That's it. It's doing the same thing, but it allows me to run down all if I wanted to and as well as dot filter cool. So give her to that. And now to actually map this in. We just say objects is equal to that one. Initialize it. So the fact that were using models, that manager means that I can still do everything that I've been doing such as objects at all, such as the creates method when we had that somewhere around here. So we don't wanna have our manager be any different than models, that manager. So we say that and now in our list view, we'll just go ahead and say Published se Event refresh and didn't come through Let's go ahead and rerun the server. And now that Blawg is also still empty. All right, so it's going off of published. But I actually want to go even one step further, and that is, I want to be able to use this right here on any of my query sets. So, in other words, going back to my views, if I did all dot published, it's going to give me an error. The queries that has no answer be published, so this is called a custom query set, and it's really simple to make as well we do. Class blawg Post query set and its models dot query sent. Make sure you capitalized the Q and S. And then this time I'm gonna use this exact same thing. I'll cut it out, paste it here. But instead of now self taught get Query said, it's literally just self dot filter. And then our model manager has to reference that so we'll define get query set. Hey, what do you know? Then we return our new query set itself that model using self underscore TV. This means that it would go off of our model and this is our new query set now. So now back in my views, I can safely use dot published and refresh in there. And sure enough, it does that if I get rid of Don published, it comes back to what it waas so naturally, I still do wanna have that method of Dot published and it's defined. Published self in this time. In return, Self don't get Cree sent, not published, so this method now calls this one that way back in my view, Aiken do dot all that published or simply just got published Either way works now, but the DOT published is very important, so that if I have a search feature, I can only get the published items as well. So that's managers and query sets. These are things that can get a lot more complicated than that. But the nice thing is that we can see at least some of the basics of how it works now. 50. 50 Published and Draft Posts: All right. So we have this published eight. If we go into our forms and we actually add it into one of our forms, like her model form and we go to actually edit one of our objects here, let's just grab one of the slugs, go in there and it edit. We should actually see that field. So let's save this and refresh. And there we go. Gotta published eight. Now I had send and refresh this page. It automatically gives me the time. So if you if you just put in a date here, it will run off of that date. And then if I go into my blog's it shows that again. That's critical mind cells back into actually showing it. And it works off of just a very simple field. Now there are other customization is that you can do to make this being a drop down and stuff like that that takes mawr Java script stuff than than what we're going to cover. So just keep in mind that if you pass in the year, the month and the date, then it will run. It might actually be different. Opinion. What time zone you're in but yet year, month date that should work just fine. And then, I mean, because if you try to do, let's say like something like that, you'll say Inter a fella dating time, right? So it's a year, month date and your so now you actually have a way to create a published post. Now, of course, you could go into the Janu admin and do it in there too, right? So if you were to want to just create them on the front end like this and then publish them , which would be really nice if you had a an author like you hired somebody, that's so they're the author and the right a bunch of posts, and then you could go through and actually publish them. That would be certainly a good way to do that. Now, something that I do want to address is when we have this blogged list view. What if I am logged in and I want to see all of my posts, whether they're published or not? Well, this, of course, could be a couple things. It could be something that simple, As if request that user that is authenticated, we could then Duke US block post that objects that filter user equals to request by user. And then that way that user on that block post list would see all of their posts, whether or not they were published. That's one way to do it. Another way to do it is to walk, to keep this like that. But then combine the query sets together so I'll just call this my qs and say the final Corey sets are equal to the 1st 1 pipe Mike us and then dot distinct. What this does is it combines cree sets of the same class, right? So they're both block posts, and then it actually uses on Lee the ones that are in there. So it's like the distinct examples of them. It doesn't have duplicates at all. That's why we call the Distinct, like the same post is not gonna be posted twice. So if I refresh that, sure enough, it shows me that. And then obviously, if I were to get rid of this block or to even go into an incognito window absolutes, try that again into an incognito window. We would only see that one post because I'm not actually logged in. So then, of course, if there isn't a post, maybe in a list in line, if it's not the detail we could then say if not blogged post dot publish date, then we could say draft, right? We see that, and now we see that it's a draft and we can change these things accordingly. I could also say something very simple and put it into my card here. And do you like BG dark and text light those air, obviously bootstrap classes and there go. So now you have something like that where it's like, Oh, clearly these air drafts. And then, naturally, we can add in some more text here and, you know, maybe like a small text and we'll do class equal to text, muted and then just do are published. Eight. So blawg Post publish date and small. Okay, refresh that, and sure enough, there you go. Cool. And this one says none. Well, that's no surprise. It's not ready to be published yet, so we can leave it in as none. And especially now that I have these different background, perhaps it's there. Maybe we do warning instead and just do text dark that way. It's like, Whoa, that's clearly a post or we just do border and border warning that way it's like, OK, cool. That's not as overwhelmingly a draft, right? Cool. So now that we've got this, we can have our published dates. We can release them. We could do all sorts of, like, better user interface stuff now that we also understand how to do model managers and custom query sets. 51. 51 Static Files and Uploading Files: Now what I wanna do is actually have away toe upload items to my block post, whether their images or files. I want to know how to actually do that. Now, this adds several steps to making this work, so we're gonna do all of them in this one. And the first step is to actually set up our settings dot pie. Now, the reason we have to set up studies up, I has to do with what's on the very bottom and that is related to static files. So static files are any of your cascading style sheets, JavaScript or images that you need Django to reference relative to your jail project. So right now what we've been doing is we have all of our CSS and JavaScript coming from a content delivery network. In other words, we have bootstrapped just linked from some other u R L. Right, So if you actually want those linked in Django, you have to have a way to handle these things. You have to have a way to handle your images, your job script in your CSS. So that's just for static files. It's not for your uploads. There's another one for up loads as well. So we're gonna go ahead and declare static route and media route. We're also gonna have the static files ders and that's gonna be a list. And then finally, we will have media. Your L Okay, so static route. This would be a live cdn of some kind. Like aws as three or something like that. More likely, when you go live, you're not gonna have Django serving your aesthetic files. That's insecure. And it's not a good idea, but we're testing things out so we can do it. I'm gonna go ahead and put my aesthetic route outside of the SRC folder and I'm gonna call this static Cdn test static CD in test. And in here, I'm gonna go ahead and make another folder and I'll just call it blink because on get hub, we'll have a blank dot txt and says blank on purpose. I'm not gonna have this these files on get huh? But the folder will be there, so this emulates what a static server would be. A static cdn server. It emulates something like AWS s three. So I actually want a reference this folder inside of it. I'm gonna make another folder called Stanic. And inside of that I'll make another folder called Media. So those are my set recruit and media route. So we'll do OS path don't join and there's gonna b o s path that dir name of the base directory, and then it's going to go into the static cdn test. So this is gonna be our local static cdn path. That's what this is, right? Yeah, Kate. And then we're gonna just use that path for each aspect aesthetic and for aesthetic route. Okay, so this is taking the place of as if it was a riel cdn like AWS s three. And then our media would be in slash media. Okay, so the next thing is, we would come in here and also have a local version of our static files so the local version can be inside of your jangle project. This is where you would like, make some changes. You Adam here and then they would be later uploaded to static route. So let's go ahead and make that folder and it's gonna be called aesthetic files. And again, I'll just go ahead and say blank dot txt In this case OS path that join. And this is gonna be now my base directory and the Static Files folder. Okay, so actually come back to the aesthetic file stuff in a little bit. But for now we have an actual place for my actual media. This is where I would upload files, and I realized that, like, a lot of these things are a little bit complicated right now because the configuration is just weird. I just added a bunch of folders, and I'm not really sure what's going on. So to make at least the media part more clear, let's go into a model and its create our image. Fields will go in, say, image equals two models and at first will just go ahead and call it a file field and I'll say, upload to and we'll just say images slash allow it to be blank and no. Okay, so we made changes in our models. We run python, managed up, ey make migrations and then Python is stopping. Why migrates? Okay, now that we've got that, let's go into our admin. No, let's make sure servers running and then jump into her admin. And now we have a field for files so I can click on this field. And let's just take something as a screenshot. Just do a real quick screenshot here and grab that. Got a file I had saving. Continue. And sure enough, it seems to have uploaded. And where did he go? Well, if we look in our project, we see that it's inside of the media folder inside of the image folder. And there it iss. Right. So upload to was inside of that media route into that image. Right? So it went into this directory, of course. Already showed you how you could see what that director is. But we now see that we can upload things there. Okay, cool. So now how does static files work? Well, in this blank dot txt if I just said this is blank on purpose. Say event. Close it. And now if I ran close out server something called Python managed up, he Why collect aesthetic? And we say you could say yes or you could just run it. And now what happens is we have our local static files here, right? So if I had CSS or javascript or other images, I could load them from there. And all of those go into that static CD and test we see static. Here's that blank file says this is blank on purpose. It also has our admin stuff in here. So this is related to the jangle and men, which is pretty cool. Okay, so now we have our static files. We have our media files. Now, we actually need a way to display them again. If we're using a live cdn something like AWS s three, they're most likely already displayed, regardless of if our Django project is in production or not, because our static files are being handled by a completely different server than Django. But, well, I'm testing locally in this case, I especially how I have it currently set up. I have to actually have a way to display this this data and we're gonna do that by jumping into our your l's here and we're gonna add it into a your l that will pretend to be aesthetic cdn serving project. We'll do from django dot cough. We're gonna import the settings here, and we're going to say if setting Stott debug so if it's not in production, this is our you know, our test mode. Then we can go ahead and add to our your L patterns and we add them by first doing an import. So I'm gonna actually keep this import inside of the settings debug Call here and I'll do from django dot com dot your l's not static. And inside of this here, we'll go ahead. And instead of having another list here, we'll just go and do static. And this is setting stock aesthetic. Your l and the document root equals two settings that aesthetic route. Okay, so, of course, those settings are the ones that we set in here. Set a crude aesthetic. You're l. And then we want to do that same thing for media. So copy this line right here, paste it in and just say media, your l and media route. Okay, so now if I refresh on this page and open this up in a new town, I should actually be able to see that image. Assuming that I have my server running. Oops. I didn't fully import the static. We'll import error. It's from that again refresh. And now I can see my image. Okay, So again, if I didn't have that. I would not be able to see my image. Although Django will still think that's where the images coming from. A. Because of how I set up my media. Your l Okay, cool. So now that I've got that, I have a way to actually see my images and upload them. That's not too bad. Now again, we would wanna use something different in deployments when we go live. And all of that is covered in the managing static files of Django itself. Cool. 52. 52 Image Field and Uploading Images: and this one. There's a few things we're gonna dio. One is changing the file field to an actual image field. In order for me to do this, I have to install Pip envy. Install pillow. So pillow is the python image library and will allow you to use the image field itself. What this will do is just validate whether or not what I'm uploading is an image like it. It does make that much of difference. So after installs, I'll go in and do Python managed up ey make migrations And then Python managed up. He why migrates? Great. So now I've got that image field. Let's go ahead and run the server again. And this time around, when I go to select a file, it's gonna have to be an image, right? It's not gonna let me do anything but an image. So, like, if I went into Django, noticed that all of these things are kind of blocked out, so I will use that image. But But I really want is my blawg New toe have that image field in there now that the initial thing would be to go in my forms and then just literally add in image here. And of course, that would give me something like this. That's OK, but if you try to run it, it's not going to do anything. And that's because, informed on HTML, we have to add in something called ink type, and this one will do multi part slash form data. You'll always use this when you wanna have data actually come through with this value with the data in your form. So now that we have that, we're almost ready to actually use this post again or the create method, However, our post block post preview doesn't have a way to get the files so simple. It's request dot files or none like not a whole lot different than Request Post. But since we've declared the in type with multiple data, we can now pass in files just like that. All right, so we say that and let's go into our block post now and I'll say Tesla's again with image. This, of course, is still just gonna be one of my screen shots here, and I'll do a B C 123 whatever published eight. Let's go ahead and use something closer to today it's Mitt. Go into our ad men to the most recent one. What do you know? There is our image and noticed that it actually has some Ah, pending string there. That's because when we uploaded this image, we already uploaded it, right? So this new one actually just adds some additional data on there to make it unique, so it's not overriding what's already there. So that's actually how you can upload images on a very basic level. Now there are certainly ways to upload giant images, but that's not something that I'm gonna cover this time. And really, it's not something you should be doing on a block post, anyway. Of course, the final thing that they need to do is have those images display inside of my in line, and that's also very easy. We just come in here, I'm gonna put it right above my title, and I'll say, if blawg post dot image, So if there's actually an image there, then I'll go ahead and do it. I m g source, and there's gonna be blawg coast dot image that you are. L. The Ural part is actually important, and then we do class equals two card. I am G top. Okay, so dot your l is going to render out the your l that we have set up in here, right? And if we were using AWS s three and specifically using Django storages to handle it, both things are certainly really good options. And also really easy to set up. It would actually give us that you are ill, but in our case, since we're testing locally, it's going to give us a different one. Noticed the image comes in and it gives us that relative your l for that image as well as if we scroll down to the other one, right? And using boots. Tramp makes that all responsive, which is pretty cool, right? And naturally, if I were to have yet another image, I could just do, like, sort of a sliver of things like that, and then blogged new and say working image, that last screenshots working image, and it's working cool. And then our date here go into our blogged, and there you got are working image, and the image is not linked right now, so I'm gonna go ahead and link it. That's probably a better idea to have it actually linked. So when they click on the image, it goes to the block post itself pretty sweet. 53. 53 Putting it All Together: now we're gonna go ahead and put a lot of these skills together in the form of an actual search. Now, with a search button, you would have some sort of query. So let's say, for instance, you want to type out working image and you would search it. And this should go to its own view, like it be its own view that handle the actual search itself. Of course, when I entered this search and question, Mark was added here. So let's go ahead and take a look at our knave bar and into the actual search form that's in there. So my input. I have this search. I'm gonna go ahead and give it a name. That name is just gonna be cute. That's often what you'll see with the search bar is the name being. Q. So now want Tapout working image? It now has an argument in here. Q. Equaling two. Working plus image. Now, if I change the action, not sure if it works anymore, but if you change the action to google dot com, possibly even with search added to it refreshing here and say, like beach image and hit enter, it takes you over to Google Notice that you can't actually search like that. So maybe, let's put just Q Yes, So it just pre fills the data now it doesn't actually perform a search. So if you had enter, you'd actually perform the search. So that's, you know, one of those things about action. You can change where it's gonna go. So in this case, I really do need it because on my locals page, I wanted to go to that search. You're l all the time, no matter what page it's on because the NAB our is gonna be on every page. So I want to make sure that this goes to that search page. So now when I do it and do working image and now goes to a search for you And of course, this page is not found, So let's go ahead and make it now. In my case, like I said, we're gonna put together a lot of the things that we've done. I'm going to do Python managed up here. Why start AP searches Now this amp, we're gonna go ahead and make a model and we'll call it class. Search query takes in models that model, I will say a user and we'll say it. Say what the query is and we'll do a time stamp like when it happened. Now, Tad in the user from django dot cough import the settings and the user is going to be models dot foreign key settings that off user model Blank equals the true Knoll Eagles. True, the query. This is gonna be HR Field. Let's do Max length of 2 20 and I don't want it to be blank. So just leave it like that. And then timestamp models that date time field auto Now add equals the truth. Great. So I have a model. Let's go ahead and make sure my app is in the settings. So in here we called it searches say that it's going and run on migration. So python manage dot p y make migrations. Ah, and of course, we've got this on delete issue. We've seen that before course on leads and this is models that set No, something I really like about Django. Is it? Dove? Give me a lot of these very verbose errors. Next thing is make migrations and then migrate. Since I made that model I might as well add it into my admin as well. So we do from the models import the model name than admin dot sight that register and we read. Sure, that model name all of this is because of my view, right? I actually want to create a view here, and I'll call this the search view and it takes in a request. And then, naturally, it's gonna return. Render and takes in the request again. The template name. In this case, I'll just call it search slash view Denise email, and I'll put it into context for now. Since I did that to go ahead and put this template in here called templates inside of the templates. Some to make another new folder called Search or Rather searches to fit with the APP name for both of them and an inside of searches will call it view dot html. This view is going to extend from based on HTML, and then we're gonna go ahead and put our block content in here and in block, and I'll go ahead and do it. Give class of cola are row first. We're just adding in some of our bootstraps stuff in here and we'll do a div class of column 10 or rather calm 12 column, eight Imex auto. Okay, and then I'll just do p class of lead and say you searched four and we'll put a query in here, and then I need to put in my results. So back, in my view, we'll go ahead and say Query equals two. Well, to get the query, we can use the request that get Paramus and I can use Don't get into the name of the query that I'm using. Now, if you go back into that Nam bar, we used the name already. We put it in, ask you. So if I called this query, you would put this in as query. Okay, But I'm gonna keep it in sq again. That's fairly common. And they want to set a default value of none. Right, So this right here is looking into the request get dictionary looking for the key of Q. If it doesn't have one, it's gonna set it equal to none. So now I can put this in as my context. Let's go ahead and call it context, okay? And then the next thing is I'll go ahead and import my model. So from that models and import the search Cree model and I will start out with user being none. And we'll say if request not user that is authenticated, then the user is the request that user. And now I could just go ahead and do search, query and objects that create user eagles to user Query equals to query or that keyword right? And if you wanted to make it less confusing, you could use Q. So this will actually save that query every single time. With my search for you. I don't need to make your L's for the sap because it's really just one view. I'll bring it into my main project, Urals, and will do from searches that views import search for you. And they will make an end point for search with Path search and in our search for you. There we go. Cool. So far, so good. Hopefully let's go ahead and run this server and let's try that search. Hey, what do you know? You search for working image. Great. So we actually just did a bunch of things quickly, but a bunch of things you should now be very comfortable with. If all of that was uncomfortable, you might want to go back and watch different sections of this. But I did it really, really quickly. So we're actually now ready to do a mawr complex. Look up using this search query. As we've seen now, the reason I actually created a model is sometimes you might want to monitor what careers air coming through and by whom. This is a way to do that. You can then go into your actual admin and look at those search queries and see what queries or what people are looking for altogether. And of course, if I have no query here, I get this error. So what I want to do then is actually just say, if query is not none when we do that, because we can't have it says not no constraint. I can't have that query field as no. So really, I only want to show that up. If there is an actual query, so we save everything go back in here, we search for none. So one final separately for this is to say, if query, then we will show that clearing otherwise we can just bring in that form again, and that's exactly what I'll do. So the form being what's in the never far and in here and the right, So now you search to that search and what do you know? Pretty cool. 54. 54 Complex Lookups: Now, for our final feature of this, we're gonna have a query that actually looks up stuff in the database. So let's go ahead and jump into our blogged model and I'm gonna go ahead and make a model manager called Search and it's going to take itself and Queary being none. Okay, So if the queries none will just say if query is none, then we'll just return. Self done. Get query set done. None. Just a way to return the query set of this type. Otherwise, we're gonna return the self Don't get query set. And well, I wanna also have a search method up here because for the same reasons of published, I might want to have the ability to search things. So self inquiry and this one I'll go ahead and return initially, self doubt, filter and say title. I exact that query. Okay, so then we would pass in this query here and with my search, I also want to make sure that they are only published items. So go ahead and do dot published as well, which will call this right here so that he will say inside of things that are published. Go ahead and do this search. Okay, so now that we've got that, let's go back into our view here. And we do from blawg top models, Import block post. And now our actual query sets or the you know, blawg list equals two block post on objects. That search query equaling two. That query. Right? So we have that dot search because I made it a lot manager. I also made it a custom query set so we could do it on other things, so I could also do dot all that search. Okay, so there's our bloodless Now, what I'm gonna do first is actually move my context up a little bit. And then in here, I'll give my context of being blawg list Equalling to blawg list. Not a big deal here, because then, in my view, we can have this in a little bit and I'll go ahead and say four blawg item in blawg list and four and much like we've seen before, we can include R block stuff. Okay, So block post in this case is blogged item truncate True detail. False. Okay, so we saved that. We do search for working image. What do you know it's working now. What if I did a search for It's working. That's not actually coming in now. A way I could fix that is by changing this from title to content and refresh, and it's still not coming through. So let's try. I contains, and I contains worked. It actually showed me it's working, so this is a little bit of a problem, and that's where these complex lookups come in. So we use something called a Q. Look up. So from django dot models import Q. That's capital que so we can actually change different lookups together so we can say the look up equals two Q and title Underscoring the score. I contains that query or with a pipe there we can do Que content contains that query and we put these into some parentheses there. And now I have a more complex look up that I can pass in to my query or to my filter itself . Of course, they could be change and have Mawr things in here like you could also include the slug, right? And the reason I'm using I contains is because we just want to check if this query is within this field. So Slug Case and hello, We've got that. Let's refresh again. Again. It's working. And now for your search for working image again, it's working now. I could also go one step further and say that I want to look up. Maybe my user like the author, right? So it's going to say user and first name I contains query or and notice that they are double underscores here. Or we could do last name or you could do the user name or email or or or right So this gives me a nice, complex look up for this item here and now if I search, let's a CFE its search for CFE I did email. Let's let's change it back to user name. Change it to use the name And they're now I can actually get all of the published items from this user, and this is also even more clear when I do this search and I'm not logged it right now. It does it in the same order. Everything's in the same order as the published eight themselves, and it's using all of the things that we've come together. But now we have a much more complex look up and await actually do a robust search for our project now. This is also where you can start thinking about how to do different kinds of views. Now that I can do this more complex search, that's really just a more complex way of doing these things. You don't always have to do it in the in the sense of like it's a search view and it's actually running some search. All right, so that's it. We have now created a very functional blawg. I realized there's some things that are missing, and we'll talk about that in the next one. But just keep in mind that all of the stuff that we've learned here can be build on top of , and this is where you then start taking, ah, lot more stuff related to Django. But I will leave you with the challenge before we talk about what's next. And that challenge would be to actually do everything we did. But four comments like making comments on any given post. I think that you can do that now because you've learned a lot about how to associate user data making forms and all that, So that's where I leave you 55. 55 Thank you: thanks so much for watching. Try Django 2.2. I realised we went through a lot in this series, and I do encourage you to break things like the only way you're really gonna learn is by number one coding and doing the things that hopefully you did that whole time. And then the number two is actually breaking things like trying new things and then getting into a state where it feels like it's unsolvable. That is, we're gonna have a lot of growth. And then finally, one of the main reasons I focused on this being a blawg is so you can actually start sharing the things you learn. We need more people teaching Django that way. We can all understand it on a much deeper level. I think that's really important as well. So, anyways, thanks so much for watching. And I'll see you next time