Learn Django from Scratch - Ultimate Django Starter Guide | Grant Klimaytys | Skillshare

Learn Django from Scratch - Ultimate Django Starter Guide

Grant Klimaytys, Software Engineer

Learn Django from Scratch - Ultimate Django Starter Guide

Grant Klimaytys, Software Engineer

Play Speed
  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x
54 Lessons (3h 18m)
    • 1. Course Trailer

    • 2. Introduction

    • 3. Important Things to Remember

    • 4. Setting up Python and pip

    • 5. Create a Virtual Environment to Keep Your PC Clean

    • 6. Setting up Django and Creating a New Project

    • 7. Starting Your Server and Site Already

    • 8. VS Code Editor and Python

    • 9. Apps vs The Whole Website

    • 10. Writing a View in Django

    • 11. The URL System in Django and Linking our View

    • 12. URL and View Task

    • 13. Solution to Task

    • 14. Setting up a SQLite Starter Database

    • 15. Models and the Single Source of Truth

    • 16. Including Apps in Django and Database Migrations

    • 17. Playing Around with The Django Python API

    • 18. Making Models More Readable

    • 19. Task Solution and Adding Additional Functions

    • 20. Introducing the Admin Interface and Creating an Admin

    • 21. Adding More Views and URLs

    • 22. Writing More Realistic Views that do Something

    • 23. The Templating System in Django

    • 24. Render Shortcut for Templating

    • 25. Raising a 404 Page Not Found

    • 26. Task Creating a Template for the Detail View

    • 27. Solution Creating a Template for the Detail View

    • 28. Removing Hardcoded URLs Better Design of Our Code

    • 29. Namespacing URL Names in Django Important

    • 30. Creating an HTML Form

    • 31. Creating a View to do Something with Submitted Forms

    • 32. Directing to the Results Page

    • 33. Generic Views to Avoid Repeating Code

    • 34. We Found a Bug

    • 35. Fixing the Bug and Testing Again

    • 36. Task Solution Adding Comprehensive Tests for Dates

    • 37. Testing a View The Django Client Makes it Easy

    • 38. Improving the View

    • 39. Adding Tests for the Polls View

    • 40. Testing the Detail View and Your Homework

    • 41. Solution to Writing Test Homework

    • 42. Setting up CSS Style Sheets

    • 43. Adding Images to Django Assets

    • 44. Task Resize the Image

    • 45. Solution Resize the Image

    • 46. Customise the Admin Layout for Question Polls

    • 47. Task Adding Question Choices in Admin

    • 48. Solution Adding Question Choices

    • 49. A better way to Add Choices to Admin

    • 50. Customise the Admin Change List and a Task

    • 51. Allow Sorting on Custom Fields Like Recently Published

    • 52. Customising Project Templates and a HARD Task

    • 53. Solution Customise Admin Index Page

    • 54. Thank You and Your Class Project

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.





About This Class

If you're looking to learn Django fast then you're in the right place!

This course covers every topic you need to get started in Django.

I'll guide you through working with the worlds most popular web development framework, right from the start all the way to your first Django application!


So why is this the best course to get started with Django?

The entire course is based on the official Django documentation tutorial. It takes that official tutorial and turns it into an easy to digest video format!

Therefore if you have any questions or just want to dig further, you can just look it up online easily!


Contents include:

  • Views and URLs

  • Database Integrations and Models

  • Django Administration Built In

  • Going more in Depth with Views, URLs and 404 Pages

  • Writing and Processing Forms

  • Using Generic Views to Save Time

  • Test Driven Development

  • Static Files and CSS in the Context of Django

  • Customising Views

  • Customising Admin Views

  • And a lot of pro developer (that's me!) experience


I'll see you inside the course!

Meet Your Teacher

Teacher Profile Image

Grant Klimaytys

Software Engineer


My very first software program was the artificial intelligence brain of an underwater robot in the early 2000's, still the coolest project I have ever worked on!

Since then I have designed and built websites, software and apps using all manner of languages and frameworks. Javascript, Bootstrap, .Net, Python, PHP - you name it, I've probably used it.

These days I focus on building quality cross platform apps using Xamarin studio in C#, Xcode and Swift 2 and Android Studio.

If you're considering becoming a freelance developer then I can honestly say it is the best life in the world. You will have no boss, earn an hourly rate of $60 - $150 and take holiday whenever you want!

Of course you have to learn how to make good apps first, which brings me to my second pa... See full profile

Class Ratings

Expectations Met?
  • Exceeded!
  • Yes
  • Somewhat
  • Not really
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Your creative journey starts here.

  • Unlimited access to every class
  • Supportive online creative community
  • Learn offline with Skillshare’s app

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.



1. Course Trailer: If you've tried to learn Django, you've probably discovered that there are quite a few concepts to get your head around. In fact, it can be quite difficult to learn Django. So what I've done is I've taken the official Django tutorial where they cover every single aspect that you need to know about in Django, including databases, views, requests, forms, et cetera. And I've turned that into a fully featured video course. You have a double-whammy. You have the video reference from me, a professional developer, and you have the official Django documentation. So if I cover something that you need to know more about, you can just click on the documentation and find that exact section. So if you've been looking to learn Django and most tutorials have just left you confused. Then this is really the ultimate resource that will get you through to that next level of learning, the world's most popular web framework. I'll see you inside. 2. Introduction: Hello and welcome to this course on getting started with Django. Now this course is actually based on the official Django documentation. So if you go over to the docs dot Django project.com, you will see that they have a tutorial section here. And I'll go over that in a little bit once I've shown you the apple gonna make, this whole course is based on this tutorial. So you have a double advantage when you take this course. You have the views of a pro developers such as myself. And you have the views of the official Django dogs. So if I cover something that isn't quite enough detail for you, then you can go into the dogs and find that area. Click on whatever you need to click and get way, way more detail. So it's a great way of doing things. Okay, so what are we going to build or what is the Django project documentation want us to build? What they want us to build a Polls app. It allows us to take polls, and I've got just a nice background image here that comes up later in the course handling images and static assets. But this Polls app creates a list of poles. So I've got a what's up pole here. If I click it, I can click on an option. I can vote for something. And when I vote, it tells me the results of the poll. How many votes, each one, God, and then we have a vote again link. Now this looks deceptively simple. It's anything but simple in the background because we have requests going in and out. We have votes, we have forms, we have databases, all that good stuff. If I clicked vote again, then we can do it again. Now, along with this, django gives us an admin section that automatically registers the models, in our case, the questions for Poles, and allows us to edit them. So if I click questions, I can go to, for example, is this the future question? I can set the date time to today and I can save it. So now if I go to my poles address, we have that new poll showing up. So you see that Django automatically picks up all of these items in our database and shows them as a list. And of course we can configure that to the nth degree. So that's the basic app that we're making. Now before we actually get going with the course, I'm just gonna run you through the tutorial as far as Django is concerned. So part one, we're going to look at requests and responses. Obviously a web app, I make a request to a server. The server responds with something and we provide a view to hand back to the user. So that's the very basics of any web development framework and it's a very, very good place to start. Then we're going to look at models and the admin site models are just the things that provide the data that we're going to show. In our case, these are the poles or the questions. Alongside that. We're also going to look at integrating the database into Django. Good news. Django does databases fantastically. We don't have to make any database connections. We don't have to do any database saving manually. It just does everything for us. It is great to get started with. Then we can just move on to views and templates. Obviously, everything you've returned on the web is a view. But we don't want to mix our views in with code, which we will do it first just so you see how it works. But then we're going to separate things out into templates. So we keep everything nice and separate. So we have maintainable code. Then we'll move on to forms and generic views. Now forms allow you to submit data just as you see in the polls when we click a vote, that's a form when we click Submit, when submitting the form. And then with generic views, there are some views that are so similar and used so often, we can use what's called a generic view, which saves us a lot of development time. So we'll go into that. Then. We're going to get to a subject called testing, in particular, test-driven development. Now if you don't know about testing, but you've heard of it. I can tell you from experience that I used to be skeptical, but now I'm sold on testing for making applications that are complex. I mean, our application here isn't particularly complex, but I will show you how to run testing so that in the future when you're modifying stuff. Because as a developer, that's what we do. We just kinda mess around with stuff until it breaks. So testing will enable you to check every time you change something that everything is working as it should. So we're going to run through them. Then we'll look at static files. So in particular, cascading style sheets or CSS that allow you to deliver those particular files that are required by HTML pages as well as images. So that background image we saw in the main application, we'll look at how to store those and how to specify the files. And then finally, we're going to look at the admin site, which is also mentioned in part two. But in this section we're going to customize the admin site. Now what you've seen in the admin site is the following, and that is pretty much all provided by Django. There are some things here that we are going to mess with, like date published and published recently. So I'm going to show you how to customize the admin site. So you can have the admin site that you really want, okay? A lot of content and it's a good few hours before actually get through it. Now a couple of things follow along with the course. As I do stuff. I'm going to copy paste code into the code editor. What I advise you to do is just pause the video at that point, copy the code down, and then carry on. It's a bit tedious if I type all the code and people get annoyed by it, so I've stopped doing that. Okay? Now, obviously this course is for you if you've never used Django before. Clearly, we're going from the beginning to get a good foundational understanding. So if you're an advanced Django user, well, I didn't really know why you're here. Maybe you need a refresher and that's great, but Django hasn't changed that much over the years. So you should be fine just giving a go at creating any old application. Anyway, that's enough of a developer's ramblings. Let's get going. 3. Important Things to Remember: There are just a couple of things that we need to be aware of before we actually get started. Now this first section of the course, your first Django app, or the entirety of the course, depending on which version of this course you are watching is based on the official Django documentation. So if you head over to their official developer docs, just search for that at Google with the keywords Django, of course, you will find this tutorial up on their website. Now, their dogs go into quite a lot more detail than I do on video. But the reason that I'm using their documentation as a guide for your first app. They make this Polls app that we're going to make is that if you need to find out something more in particular, you want a deep dive on a particular subject and you can have the docs open at the same time as you're watching this course. So you can go ahead and do that deep dive without me having to explain it and hold everyone back that just wants to move ahead and doesn't actually want to know those nitty-gritty details. Ok, so that's what it's based on. Just head over to the docs and you'll be able to find everything in this course. The second thing is we learn by doing, humans are doing creatures. We have to do stuff to understand it. So throughout the course, initially you won't see much of this, but throughout the course, you will see tasks and solutions here in there. When I give you a task, it's going to be very, very difficult at first. But once you get the basic scaffolding app up and you look at the previous videos where I've covered the prerequisites to that task, then it will start to become clearer. And that's all part of your journey as a software developer. You can't just watch videos, you have to actually do it. So I want you to follow along with everything I'm doing. Feel free to pause the video. There's no guy with the time as saying you can only spend 30 minutes on this section. Pause, take your time. If you get these basics right, then you will become a professional Django developer. Because in my opinion, a professional is someone who takes the time to get the basics right? Okay, and finally, when I give you a task, please attempt the task before you look at the solution, okay, they'll just be tempted to skip ahead and look at that sweet, juicy solution that gives you all the answers you need. You need to think about it. Finally, finally, if you're enjoying everything you see and you think I'm explaining things well and you're learning stuff every single day, then please leave me a comment and or a review. Let me know which parts you liked, which parts you didn't like as much and what things you'd like to see changed in future courses. All right, let's get going with all of the techie stuff. 4. Setting up Python and pip: Alright, there are a couple of things that we need to first get started with Python. Of course, we have to install Python itself. So if you just search for Python, install on Duck, Duck Go, which is a better search engine than Google. If you were wondering, right up at the top of the page, you click on under downloads, we have download Python 3.9.1. Now, a word about Python versions, if you look down here, we have everything from 2.7 and each version upwards 3.63.7. Okay, so too is a major version, three is a major version. You would only install these previous versions if you were looking to create an application that didn't have a package that wasn't needed, a package that wasn't compatible with say, 3.9. Some people have that requirement. We don't have that requirements, so we always go for the latest and greatest in the release status. You'll also notice that Python has an end of support. Let's just zoom up on this of 20-25 for version 3.9. And of course, the previous versions have their own expiry dates for support. Hopefully by that point you'll know if your app works or not. And so you can upgrade to Python for when it comes out. I wish you the best of luck if you attain that. Okay, so 3.9 is what we're going to have. We're just going to click Download. We're gonna keep that and we are going to install that once it has finished downloading. Now because I'm on Windows, there are certain things you need to be aware of. The primary of which is down here where it says Add Python 3.9 to path. You want to check that so that we can access the Python terminal whet, whatever folder where in Windows has this thing. Or rather the command line in Windows has this thing where you can only access these executables if you're in that particular folder. If you add them to the path, then you can access them anywhere. It's not a problem you have on Mac or Linux or anything, Unix. So we're just going to go ahead and install that now. It should give you an admin request warning. And once it's installed, we are then going to install the next thing we need right now that that's done thanks to the magic of time travel in video, we can close that. Now we're going to install a Python package manager called Pip. Pip just makes it a lot easier for us to download and install the various packages that we need. So what we're gonna do is right-click the Windows key and we're going to go to Windows PowerShell, not the admin version, just the regular version will do for this. And the PowerShell is going to allow us to interact with the Python shell. Okay, and we're going to see a lot of this use of the PowerShell. You can also use the command prompt if you so wish. But PowerShell, We'll do perfectly for us. Okay, wherever you land up. So I'm in my user folder here. You're going to type the following command, PY space dash m space Pip, space install, space dash, dash upgrade space pip. So what we're saying is we're going to access Python using the PY prefix there. And we're going to install pip. And if we just press enter on that, everything should work fine. If you get an error here, double-check that Python is installed and double-check that it is installed in your path as well. Alternatively, restart your computer first before you really dive in and see if that doesn't fix the problem. Beyond that, jump onto Duck, Duck, Go and search for your whatever your particular problem is, right? So we've installed the two dependencies. That's Python, that's Pip. We're ready to carry on. 5. Create a Virtual Environment to Keep Your PC Clean: Right, it's time to start working on our actual project. But just before we get there, we need to set up something called a virtual environment. Now, that sounds a bit scary, but it isn't really in reality. All we're doing is setting some flags someway, some variable to say, when you install packages for our particular projects, you're going to install them just for the application that we are working on, the one in this folder. We haven't created the application yet, we will in due course, but we're just going to set that flag. Why do we do that? Well, if we don't do that, Django or Python is going to install these packages into our system globally. So then any application can access them. That might sound like a good idea, like, for example, installing Photoshop for all the users of your computer. But with packages, they update so often and they change so often that when you have a global package that serves all your apps, you can't update it in one app without it affecting all of your other applications. So that's why we use a virtual environment just to keep everything separated. That's the whole reason that things like Docker have come about. Virtual environments are much, much better suited to actually deploying applications and getting rid of those horrible bugs around deployments and virtualization and what have you. So we're going to do this pretty simply by going into our folder. So I have created a source code folder here, and I'm just going to copy the last part of that address. And I'm going to cd into it like so. So now I'm actually in that directory. Now if you're on Windows, I think you can hold down shift and right-click. And there's something about opening a command prompts. I can't remember exactly what the shortcut is. Forgive me if I can remember that. But nonetheless, here we are in the particular directory we're interested in. Now what we need to do to create a virtual environment. Well, we're going to do the following. Py dash M, V, N. And what that's gonna do is set up the virtual environment in this folder. So at the moment, this folder is totally empty, but when I present a, It's going to go ahead and do what it does and create an end folder in here. So it's not quite done yet. You'll notice it's done on the command line. When that completes, there we go. And then in the end, we have all of these bits and pieces, things that we will never ever touch. Hopefully unless you have some horrendous bug you need to fix. So we have site packages, we got PIP, we got all kinds of things ready to go in this end folder. Now when we install stuff, it's just particular to our particular application. If you make a different application, you'll have to run through this process again. And it's good practice to always use a virtual environment. 6. Setting up Django and Creating a New Project: So now we're ready to install Django and start a new Django project. But the very first thing we have to do is we have to activate our virtual environment. And that is done. You doing the following command. Dot slash the directory that you put your virtual environment into. Backslash scripts. Backslash activate. Now if you're on Mac or Linux, this might be different. I can't remember exactly, but just look up how to activate a virtual environment in Python and it will tell you, when you press Enter, you'll notice that over on the left-hand side, we have the ENV shown there. Now, if you don't do this, chances are it's going to install everything into your global Django or your global Python environment, which you don't actually want to happen for reasons of explained earlier. So now we're in the environment. We can go ahead and use pip, install Django. And that's going to go off and find the Django package. I've already got it installed in this particular item. So we've got a warning here. That's fine. That's just pick version. So Django is installed inside of my virtual environment. Now we can double-check that by going to the Python shell just type PY. Here's a good point to check that your Python version here matches the Python version you installed. If it doesn't match it, you might start to get some weird errors. So I'd advise you may be to get rid of one version of Python and just keep one on your PC. But that could introduce errors are found. Python is very, very buggy and finicky when it comes to this kind of thing. Perhaps I don't know enough about Python, but hey, anyway, check that version, make sure it's the same version and you should be good to go. What we're gonna do here is import Django because the python shell has no idea about packages, right? So you have to tell it every time, just like you do in code to import a particular class. If it works, you get no feedback. It just goes to the next line. If I tried to do something that's misspelt, you should get that kind of feedback saying that module doesn't exist. Ok, so that's how we know it's installed. We can also print the Django version, so Django dot git version. And there we have it. We're on 3.1.4 K. So Django is installed, we're ready to begin. And the way we do that is by using the Django admin from the command prompt. So we first want to exit the python shell, and that's how you exit, exit method and go back into our shell. Now we're going to use the Django admin to stops a project. And we're just going to call this whatever we like. So I'm going to call this DJ-1 for Django one. Press enter. And that has now created the project. So looking at our folder, of course we have our environment directory, but you also have this folder that is now being created called DJ-1. And if you double-click that, we have DJ-1, which is the app. And we have managed, which is the command line API that allows the Django project to be managed from the command line. We're going to use that quite a bit in the upcoming sections because it allows us to interact with our project right here on the command line. So we can be sure that what we're building, what kind of database structures we're putting in place actually work before we have to actually get into code. Because a big problem you face when coding for anything is that what you think is happening isn't actually happening. Something else is happening in the background. And having a command line or shell that we can dive into and put commands in without changing our code is great because we can start to test things and then also write tests for our particular application, which we do of course cover in this course as well. So that's the folder that's created. If we go into DJ-1, we have Python files here, each of which do different things. And I'll get into each one as and when we need to know what they do. 7. Starting Your Server and Site Already: Alright, let's have a look at our current website as it currently exists. The first thing we have to do is go into that folder that we've created when we created the stop project, which of course is cd, cd space, D-Day one. Okay, now if you want to see what's in here, you just type ls and it will show you exactly what's in there, which is basically this managed PY and we have another folder. And of course, because we're on Windows, we can just look at it like this. So DJ-1, we have D21 and manage. We went through that last time, didn't we? Okay. So how do we know all this is running and it's going to work. Well, let's type Python, manage dot p y. And then we are going to run the server. His answer on that. And that's going to load up the development server that comes bundled with Django and Python, whichever one it is, I think is Django. Now, when you publish your jangle app, do not Hughes this. It's very tempting to, because it's built in, but it is not built for scale, it's built for development. So you want to choose something like Apache or engineers to do that. Ok, So that's running now, I'm just going to open up a browser. And what we're gonna do is head over to the following address, 127 dot-dot-dot No.1. And then we're going to open a port which is 8 thousand. That's the default port that Django runs on. So there we go, we get the installed work successfully and we know this is Django. And if you click anything, it actually takes you to the Django website. Okay, so that's how website, as it currently stands. We're only seeing this page because we've set debug equal to true. We haven't actually set up any pages yet. Now the tutorial that I am going to follow is this one, tutorial, a polling app? So if we open that in a new tab, this is what I've abstracted away from Django is documentation and made into the videos that you're currently watching. So if you want to follow along more closely, then you can have this open at the same time. I do warn you is kind of a bit drives a lot of reading. I know people don't like reading. A lot of people prefer videos, at least from what I've seen from teaching people. So if you want more details, they're there. But you can primarily rely on the videos that I'm going to create. Now, if you go back to the shell and you want to escape the server, just hit Control C, sometimes twice delta y. And inside this command here, you can give it different ports. So I can give it 8080 instead of 8 thousand. And now I can go to that particular port. So if you have conflicts where you can't use port 8 thousand on your local host, then you can actually change the port in the PowerShell and then hit control C to escape that. Ok, see if creates the Django app, we've run it, we've seen as a built-in web server. And we've got the development version up on our screen. 8. VS Code Editor and Python: The final thing we have to install before we actually get down and dirty with the code is a code editor. I'm going to use Visual Studio Code, but there are many, many options. Just search for Python editor on any search engine and you'll find perhaps at different one that you prefer. I like Visual Studio Code because there are many, many plugins, far more plug-ins than any other code editor out there that allow you to do things like pause your Python, lintel Python, just all these technical things that we have to do eventually. So obviously, I've already got Visual Studio Code installed because I'm a developer and every self-respecting developer has it installed. Okay, now I've opened it up. But what I'm gonna do is search the extensions in the marketplace. So that's this tab over on the left lower section here, or control shift x. And I'm going to search for Python. If you tap the first result, that should be from Microsoft, and that's the one we want. We want to install that one from Microsoft. We also have Python language extension for VS Code, which isn't highly rated but is installed a lot. I guess they gotten that before Microsoft did. But we want the MS1. There are other things that you might want to get, but we will deal with those as and when we come across them during the course. So that's it, Visual Studio Code and Python from Microsoft. And we should be ready to go. 9. Apps vs The Whole Website: Right, with Django, we have a concept called apps, which is a bit of a confusing name if you ask me, but Django is pretty old, so I think it was around before apps or even around. But don't quote me on that. What Django means by apps is a collection of code that performs a set of functions, is what a traditional developer would perhaps call a class, maybe even a namespace depending on what you're working in. So a Django application can be composed of multiple different apps, or it can be composed of a single app. You can use apps across different applications in Django. So if I have one application for poles and votes like we're going to create in this course. And I have another one for Pokemon GO. I can have an app that just accesses a Web API, brings back results and gives me those results. And I can share that app between those two different applications. Okay. And you see what I mean when I say APS is a bit of a confusing word because application apps, all you need to know is that an app is really a logical collection of functions, parameters, all the other code goodness that define a, a sort of logical set of items just like a class as I say. Ok, so what we're gonna do is create our first app. And this app is going to allow us to create poles and view poles on the internet. So again, to have Python managed dot p y. And we're going to have start app. And we're going to give the app name, which is poles. Okay, so you get no confirmation. It just loads it up for us. But if we go to our folder of source code, so we have our environment, we have DJ-1, double-click that. There we have poles ready to go. And if you double-click that, we have a whole bunch of files in there, that of course we are going to start playing with. So that's how we create the first app. Now in this project, we may or may not create other apps. If we have an app that allows, I've no user account viewing, then we might create an app for that. So we'd have Python managed piecewise dot app, user account or whatever you want to call it. So you see what I mean, apps are just logical collections of business logic views, et cetera, inside of our app. Alright, let's get a move on and write a view in Django. 10. Writing a View in Django: Alright, we've created our main application and we create our first app, which is the Polls app. So it's time to put a view into that Polls app. And we're just gonna put a very basic view so I can give you an overview of what needs to go into a view. Because remember, we're not just creating a view here, we're actually creating a response to a request on our server to provide some view back HTML. So there's a little, it's a little bit more involved. Okay, so to get started, let's open up Visual Studio code. Let's go to File. Open Folder. Goes to our particular app. I would advise not opening the ENV folder but go into one more level of DJ-1 so we can see the app and the application. When it's loaded, you'll see the DJ-1 application and the Polls app. And of course, we don't really want to mess with the DJ-1 for now, but we will go in there. So I'm just gonna close that because what I find a bit annoying with the Django file system. And maybe it's a Visual Studio code problem as well, is, you know, I, I might hit one of these things up here by accident instead of one below in polls, each got to be aware of what files you're opening and closing. So we're gonna create a view. So let's open up the poles views dot p, y. And inside here we're going to create a view. Now this tells me we don't have a python interpreter, so we can go ahead and select one if we wish. I'm going to select the one on my system and that should be fine. So what I'm gonna do is simply paste in my code and then walk you through it. So I'm going to shift tab everything backwards. Several fits nicely and if we save that, the areas should go away. Okay, so what have I imported? Well, first, I've imported from the HTTP namespace or class, the HTTP response. What is that? That's basically the class that gives us a response when we ask the server for a page. And what we're defining down here is a view called index. Every website has an index page is usually the front. Index.html. You should be familiar with that syntax. And what we're gonna do is return a simple response that says helloworld, you're at the poles index. Okay, so that's all that that code does. Now note we haven't set any URL's yet. So there are no web addresses that correspond to this. So that's what we have to do. Next. We have to just look at my notes here. Creates a file called URLs dot p y inside of poles. So let's right-click Polls, New file, URLs dot P, Y. Now what are we gonna put in this folder? Or we're going to start mapping these views onto the URLs. So if I copy the code over, feel free to pose of course, and write the code down. But I think it's a lot faster for me to do it this way rather than having you copy the whole thing as I slowly type it out. So here we go, I'm gonna save that. So first of all, we're importing this path module that has to do with our URLs. And we're importing everything from views that so the URLs dot PY knows where the views are. Because down here in the URL patterns, we're going to say if you get a path, that's just nothing, just playing path. The bog standard web address for the homepage, in this case, four poles. You're going to go into views and you're gonna find index and that, It's going to find this. And we're gonna give it a name of index. Okay, so that's all this is doing. It's mapping the URL that comes in to which view it should go off, fetch and passover to be rendered on your client side. Unfortunately though, that's not all we have to do because URLs in the Polls app are not the end of it. Our system doesn't know about the poles and the poles URLs just yet. So we're gonna do that in the next section. 11. The URL System in Django and Linking our View: Last time I said this URLs dot P Y, which sits under the Polls app. It just kind of floating in space the detail one overall application doesn't know about it. So we have to tell it that this URL, this URL patents exist. And we do that in the DJ-1 folder under URLs dot P Y. Okay, so I'm just going to remove this boilerplate commenting that's there. And we first have to import something from URLs. Cold include, which allows us to include other URL patents. And you'll notice this looks very similar to what we have in URLs dot PY. We have the same URL patents that the system is looking for. So we already have a path in here called admin. And this is the default admin that ships with the site. We'll get onto that in a couple of sections time. But in the meantime, I'm going to paste in the following line. I've got a path called poles. So if you go to the website.com, if you went to the website.com forward slash poles, it would match this path first. And that's an important thing to remember, is that as you go down these URL patterns, Django will match the first one. It sees, not the best one because there is no best one, it just matches or doesn't. So you have to be sometimes a bit aware of the order they are running things in here. Either that or you need to start renaming stuff to be most sensible and not match twice, okay? So if you get funny responses on your pages that shouldn't be there, that might be a reason why. Then what this is going to do is include the Polls app URLs file. So the detail one app knows what to do when you type in the include method here it goes and finds this, and then matches whatever is in here. So if you enter an address is going to match up to this point, right? So I'm gonna save them. The remainder of the adress that gets passed over is sent to here. So this doesn't get website.com forward slash poles. This just gets whatever comes after pose. Ok, so if I had website.com four slash polls forward slash index.html, then all this would get is the index.html part. So you can see how Django kind of breaks down the URL system. You've got the top-level domain, then you've got all the notes, sub-domains, but the various folders, SAP addresses, what whatever we call them. I'm a web developer. Anyway. So that's that pretty much done. So what we can do now is head over to our command prompt. And we can run the server that's running. If there's ever an error in your code, it will probably show here and when you load the website. So let's go to 8 first thousand of all. And we get an error because that doesn't exist. But it does tell us in tiny, tiny writing that poles and admin exists. So we can try forward slash polls. Helloworld, you're at the poles index. And that of course, is the view that we created previously, where returns that HTTP response a k. So just to clarify, because URLs are very important in Django and the URL system in particular. And you can kind of tie yourself up in knots if you don't get this right. That's the base address. That address when it's entered, goes to the DJ-1 URLs dot p y. So this is its first port of call when it's looking for matches. Then it says, right, I have extra items on the line here, I have poles. So what it does is match this path and then the system stops there. It doesn't continue at all, right, once it's matched, it stops. It goes off to the polls dot URLs. And what does it pass over to pose dot URLs? Well, there's nothing after pulse passes, literally nothing. And because this first path matches nothing, then it's going to call the index view, which is going to render the text that we saw on the screen. All right, there you go. You've created your very first app and view. 12. URL and View Task: Right, it's time for a little bit of homework. So what I want you to do is provide me with a view that responds to this request forward slash polls, forward slash test. If i press enter on that, of course, I get nothing page not found for O4. Incidentally, we're going to do our own fluorophores much later on. And I'll, I'll walk you through all of them. But at the moment, this doesn't work. I want you to create that view and creates the URL configuration that points to that test page. And then just output a bit of texts, you know, something similar to that, whatever you feel like putting in. So good luck and don't move on until you've actually tried this. I know people like to cheap, I like to cheat. I like to look on Stack Overflow and just steal people's code outright. But it doesn't help us because it doesn't allow us to understand things when we're learning. So please stop the course now and have a go at this. 13. Solution to Task: So how did you find that? Well, it should be fairly straightforward because it's actually a case, a simple case of copy and paste. So basically define another view in here, and I'm just gonna call this Test view. And then we're gonna go. Test works. I find sometimes in development is actually handy to have a view. This is any application really or, or a response of some sort for a bug, standard HTTP request in this particular case. Why? Because when you're debugging complex applications, you sometimes don't know where the error lies were. Facts is usually don't know where the error lies. And this test function just simply rules out the bug in the HTTP response code. So I do actually like having these other ones we've made this, we're going to remove it. Okay, so let me go to our URLs dot p, y, and we're going to copy this. We're gonna paste it in here. And we're going to name this test. And we're going to pass over the views dot test. And you'll notice that Visual Studio code actually picks that up again to give it a name of test. Now that should be it because URLs dot P Y is going to forward that straight to the polls URLs. So let's try that out. Forward slash polls, forward slash, test, and test works. So that's the beauty of the URL system in Django, is that we don't have to change this main file in each and individual application. We change it in there. So it's got good separation at something I like about Django. They try their best to separate all the concerns around your app. So these URLs, it doesn't matter what comes before them, right? So I could change the website name. And that's the whole idea behind apps that I can reuse. These, this Polls app, indifferent, other applications, web apps that I'm going to make, and the URL patterns will stay the same no matter what the top-level domain is changed to. Okay, so that's the solution to the task. I hope you found that relatively straightforward. I think I didn't tell you that you could actually put these in an array. But the fact that we have a square bracket here and here tells us that it's an array. And if you don't know that, then you should be taking my basic learned to code courses in Python because I explain all of this in those. So in the DJ-1 URL patterns, they will too in there that we put in. So that should have been a clue that you could also put two in here. 14. Setting up a SQLite Starter Database: One very common need for all web apps or pretty much every applicant or making any language on any platform is to have a Datastore. And the most common of those is to have a data base because it makes looking up values really quick and writing values relatively painless. Now with Django, It is an unopinionated system. What do I mean by that? I mean, you can use pretty much any database that you want to use with Django, although it does shape with automatic SQLite integration built-in. Now SQL Light is a perfectly good database for small applications. If your item isn't database critical, your application is a database critical, then SQL Light is the way to go and that's what we're gonna do here. But be aware that you can use Postgres, you can use MongoDb. There's just so many things you can use. So how do we know what our particular application is using? Well, if we go to DJ-1 folder and go to Settings dot p y, and we scroll down, there is a section on databases. And this is the default database section that we have here. And the engine belongs to the Django DB SQL lite three, that's an open source database. The name of it is the base directory, so whatever directory you're sitting in. And then db.json SQL lite three. And if we look at our folder, so source code DJ-1, there we can see our SQL lite three files. The database sits in a file inside of our application, and this is how it's going to sit in your final application when you release it into the wild, it'll sit on your server. Ok, so that's the database that we're, that we're running currently, we need to run one more command on the command line to make sure it's ready to go. And that is Python managed PY migrate. And you can see what it's done here. It's applied migrations, admin o of content types sessions. So this is just basically setting up the database so we can use it within our app. All of these items should say, Okay, if they don't say OK, then you have very big trouble, very big problems. And you might have to search stack overflow, the classic place to fix these things. So that's all about databases and we've set up our SQLite database. So what we have to do next is create what's called a model, IE, a blueprint that creates an object and that object is stored in the database. So we're gonna do that next time. 15. Models and the Single Source of Truth: Now we have our database up and running. We need some data to store in the database. We can't just go willy-nilly and store stuff straight away in the database because we need a structure that translates to the tables and rows inside of the database. Now how do we do that? Well, we're going to look primarily at this file called models dot P Y, which is in the polls folder. And I'm just gonna paste in some code here. We don't need that first part. Then I'm gonna run you through what this code actually means. Okay, so first of all, we're gonna create a class called Question, because our app is all about polls were going to ask a question in a pole. And so we need to store that in a database as well as the results of those polls. So what we're gonna do here is define a property called Question Text. Obviously this is the text of the question we're going to ask. We're going to give it a type of a character field. This is similar to a string, as you might see in other languages. Now, this uses the Django database models class to ensure that the items, the types that we're typing here match what can actually be stored in the database. If I didn't have that, we might actually run into issues, not least of which it won't find it in its current state. So we do that to make sure we're using the correct types of things to store in the database. A character field is just a bunch of text, numbers, strings, that kind of thing. And we're going to give it a maximum length of 200. If you've done anything with databases, you know what that means? Then we're gonna have our good old classic favorite, which is storing dates in a database, the pub date. And so we're going to grab the models datetime field. So that includes the time with the date. What we put inside of here is the human readable name. When we access it elsewhere, we can have the human-readable name pop up, which means we understand it. To be honest. As a pro software engineer, you shouldn't really need this kind of thing because you should have everything in the name of the variable itself, but that's just me. So each question is going to have not just text, but a bunch of choices. And good database design means that you don't put your choices in with the question in the database. You have a separate table that holds all the different choices because you could have a choice that's shared between different questions, okay? So wherever you can, separation of concerns is key. Now this choice references a question, foreign key. So this is gonna go look in the question table. And if we have the foreign key for it, which we should, it's going to pull out that question, okay? And then this parameter here is on delete models dot cascade. All that means is when something gets deleted, Look at the parent-child relationship and remove things that are no longer linked to anything else. Okay, so if we deleted a question with a bunch of choices, we might want to remove all those choices as well. That's just a database thing. I'm not a pro on SQL databases. I tend to use stuff like Mongo or elastic search for scale. Anyway. So then we have the choice text, which is the text that's shown for that particular choice, a character field, again, maxlength of whatever. If you exceed these maxlength, when you're feeding stuff into your database, you will get an error and it won't write to the database. So you need to be aware of things like that. And then votes is an integer fields. This is going to count the number of times this has been voted for. An integer is just a whole number. We're going to start with a default of 0. And every time we write to our database, it's going to increment that by one, but we haven't put that logic in yet. We're just simply declaring it here. Okay, so that's the database models set up. Now, we can actually go on and start playing with the database. 16. Including Apps in Django and Database Migrations: Now our main application, DJ-1, doesn't actually know about this Polls app properly. It especially it doesn't know about all the models that we've created. So what we need to do is tell it explicitly that there is a conflict that it needs to pick up for all of these apps, database models, et cetera. So what we're gonna do is go to DJ-1 settings dot p y. And we are going to look for installed apps. So we have a Polls app, and currently the URL points to the Polls app, but DJ-1 doesn't know about it explicitly. So what we're gonna do is just drop in a line at the top, which points to the polls, dot apps, polls, config. And that is standard syntax in Django for pulling out the config for pulse. Once we do that, it's actually going to grab all of the models and the various things that we need to make our database work. So once you've done that, we can head over to the command line and we can go to Python, managed dot p. Why? Make migrations for poles? And when you hit enter, this is going to tell you what it's done. Created a model of question and a model for choice. Ok, so what is make migrations? Well, every time you change your models or you update your models, Django will go and look at that and do sort of a make a bunch of code that's going to show you what's going to change, how your model is implemented in the database is going to add the extra columns, subtract them, whatever it's doing. This is fantastic, right? In other systems, for example, room database on Android, which is running on sql light. Actually, you have to see all of this stuff manually, which is a very big pain every time you update your database schema. So Python makes this really, really simple. It's just one more step once you're happy with what it's done here and you can actually go in and look at this code. So poles migrations. One. There it is. This is what is actually going to do. So it's going to create a model, going to create two models, put a name to it and put some fields to it. So that's what it's actually going to do. It hasn't done that yet because we need to call migrate on that python managed BY migrate. So it does that. And there we go. We've applied the poles. Okay, so now the models are ready to rock and roll inside of the database. We're ready to start playing with the database right here in the Python shell. 17. Playing Around with The Django Python API: Now we have a working application and we have a database that we have no way of actually getting data in the database via our application just yet that is coming up. So what we're gonna do now is play around with the Django command-line. So that's pretty easy to get into. And actually if I just do the following, we want manage PY shell. So that's gonna take us into the Python shell. And it has loaded the environment variables using managed dot PY for our particular project. So we have access to everything we need. Now the first thing we have to do is to import choice and question from our poles dot models because the command line, the API doesn't actually know about these things in our apps. It's kind of a dumb terminal. So you have to tell it what you wanted to import and the various things of course that you wanted to do after that. So if there's no error, you just go to the next line and that is now imported. Now what we can do is we can query the database to see if any questions our choices are stored in it. So I can go to question dot objects dot o. And it tells me there's a query set of an empty array. That means there are no results for questions. That's as we expect it. You wouldn't expect computers to put a question in your array. Otherwise, we'll verging on terminated to levels of artificial intelligence. Okay, now I'm just going to copy over the next line here. What we're gonna do is pass over a time zone. And to do that, we have to import the time zone class from Django utils to go ahead and do that. So once we've got that, we can now create a variable q. That's a question. So Q is equal to the question types. So we're gonna create a new question and we're gonna pass in the parameters that needs question text is what's new. And the pub date is Time Zone dot now. So that's why we passed in that time zone answers ready for use here. Now when I press enter on this, it's going to create that question, right? So we get no confirmation on that. Then what we have to do is call q dot save and that's actually going to write it to the database. How do we know it's written to the database? Well, if you recall, in our models, right, let's stop messing around there. In our models in question, we only have two parameters, right? Okay. But I can now request Q dot ID and I get an answer of one. Why is that? Because that's the identifier that has been created automatically in the database, and that will increase by one each time you add something. It should also increment like most databases do. Okay, we can also access other properties, of course. So I can say what's the q-dot question? Dot text, right? What else can we access here? We can access the pub date. Interesting to see what that looks like. So it tells you this is a datetime object which is a, I believe that's the SQL type that gets stored in the outbreak. Could be wrong on that. Not quite show, but it's done. It's broken down by year, month, day, time, and time zone info, which is UTC, in this case. Ok. So we can also change things in the command line. So if I want to have q dots question, Actually let's just scroll up dot text. I can say equal to something else so I can modify things and then save them again. So I can say What's up. And then that won't take in the database unless I call q-dot save. So now it has taken in the database, it is stored away for later use. Now if I go back to my original queries, I'm just pressing the up key here. If you wondering why it's moving so fast, question to objects dot o. And now we can see that we actually have an object in our database, just one. But obviously, the more you have, the more it will display that. So now let's create some of the choices. So in our models, we have a question and we have a bunch of choices, right? So let's do that in here and I'll just copy over my bits and pieces. So first of all, there's this thing. So I'll just put it on a new line. Well, there would be this thing if I could press copy. There we go. So we already have queues are question, but sometimes you'll log into this API shell and you will need to pull out a question from the database and modified or whatever model you're working on. It's worth noting you probably won't do this in production. You'll just do this in development. Queue. Here can be set when we query the database. So we're getting our question objects and we're guessing the primary key, that's what pk stands for if it is equal to one. So now q is equal to that particular question. I know we said it here before, but sometimes you'll login and you will create a question. You'll have to actually pull it from the database. Okay? Now we're going to do the following. While I say we're going to do the following, There we go. Q dot, choice-set, dot or ok. So there's nothing that we have no choice sets for that particular question. And if we are looking here, the choice set is the list of these items. Let me explain that a bit better. Okay, so we have a question which has an id. We have a bunch of choices which have a foreign key from a question. So what that command is doing is saying, what I want you to do is find the choices that have a foreign key of one in this case. And of course there are no choices, so it doesn't find any. So we're just making sure that's all working fine. So I'm just going to copy paste the next couple of lines. Feel free to pause. So we're going to choice set, create one of them. Equals not much, and votes is 0. So what have I done here? Well, because I'm calling this particular question, I can skip straight to choice set and I don't have to call the primary key. The shell knows that the primary key is one in this particular case. And so it just sets that primary key is one and then adds the other variables, the other parameters here. So that's added and then we're going to add one more, which is choice texts the sky. And finally we're going to add another one, but with just a very small difference. This time, we're going to assign that item to the variable called c. Q dot USA dot create. Just hacking again and then votes equal to 0. So now we can access the C object and do things with it if we so wish. I can press enter on that. So now because we have this relationship here with the foreign key and the question, I can say, hey, choice C, What is your question? And it will tell me the question object is one that doesn't give us much information, but we will fix that shortly in the next couple of lectures. Okay, that's a good place to end at this point. So the whole shell thing here that we're playing with, like I say, is very handy when you are developing. So usually when you're developing apps, you might feel anything like me, you like to make the views first. I kind of like to see what it's gonna look like. And then I create the data models behind the views. Usually not always, but usually. Because I have no way of entering data yet and I have no dummy data. I have to create it. And this shell is a very useful way of creating it. If you've ever created, let's say, an Android app or an iOS app, you have to plumb in the whole data entry point, the whole data entry process before you can actually see that data in the views in practice is not that much of a problem. But I prefer this way of doing things because I can put the dummy data in and see how my views react before I go build the whole admin side, which is actually quite easy in Django anyway. But it allows me to test things before plumbing in the data entry side. 18. Making Models More Readable: Let's make our model's a little more readable. So when we were setting the question dot objects dot all, it would just show us a, an array of questions with an identifier in them. As useless as a human, what we need to do is have a readable item in the question that displays on the command line. So what we're gonna do is add these two lines, two festival The question models. So we had to define underscore, underscore, that's two characters, they're string self and we are going to return the self.position dot text. So it's actually going to give this back. So instead of giving us the ID is going to give us the text of that particular question. So I'll give you a second to copy that down. Okay? And then if we go to our PowerShell and we open up our Python shell, now if you're already in the shell, you might have to exit using exit parenthesis and then go back in. Sometimes this is a bit, if it works, it doesn't work. Okay, so we're in the shell. What we have to do first of all is import everything like we did previously. So from the poles models we're going to import the choice and question. And then we're going to give it the question dot objects, dot o. And now instead, whoops. Of an ID, we have the question text. So that's a bit more readable for us. We can actually see what's going on there. Ok, so here's just a little task for you. I want you to perform the same operation on the models are rather on the choice class to define a string, to return something that makes sense to you as the developer looking at this and debugging it. Good luck. 19. Task Solution and Adding Additional Functions: So that little tasks should have been relatively easy. I'm just going to paste in my particular solution here and tab it all up, right? Save it. So again, defining that same string and returning the self dot choice dot text. So now if we go to the shell, we can import those items. And then we can go to choice dot objects, dot all. And you'll notice we don't have any changes and that's what I was talking about. What you have to do is exit the shell. You have to go back in again. You have to import your models. And then come on. There we go. Then we have to put our choice query. And so there we go. The choices are now the text of those choices. Choices trace texts that we said. Okay, so that's fairly simple. Uh, but now we're gonna move on to something a little bit more advanced. We're going to start adding functions into our models. So let me just grab copy, paste these items in here. So first of all, we're going to import the datetime in the model. So up here, we need these two items, the datetime type and from Django utils we're going to import the time zone. Remember we did this inside of the shell previously when we were just messing around. I think it was two lectures ago or something like that. Because we're going to manipulate dates and times. And where are we going to manipulate it while we are going to manipulate it in the questions model. So under def here, we're going to create another function to see if that particular question was published recently. Because on your website, you might want to show whether a question was published recently or a long time ago or isn't published yet? So we're going to return the pub date. We're going to return what we are going to return a boolean. First of all, it's not obvious in Python what you're returning. Sometimes. I'm going to return a boolean if the current pub date is greater than the time zone now, so the time right now subtracted by one day, okay? So datetime allows us to do time delta, which is just a difference in days. And this is quite, it's quite a handy feature, actually. Python slash Django or this datetime stuff, because it's much easier than other languages where you need a library to actually do this. Anyway. So this is going to look at today's date and time, subtract one day from it. And if that's true, it'll return true, which means it was published recently. If it's false, IS today's Old, it'll return false. So we can use that Boolean operator later, right? So we're not actually going to use that just yet. We're going to use that in a future module when we're actually looking at the views and setting up what we displayed to the end user. 20. Introducing the Admin Interface and Creating an Admin: It's time to go look at the built in admin interface for Django. This is one of the best things in Django because many web development systems, frameworks, et cetera, tend not to come with this so well plumbed in. As you create websites is just from my personal experience. You will tend to start with something that you need to change on the backend manually, sort of, for example, like we have been doing on the command line. But as your website gets more advanced and perhaps as you hire people, you'll want them to have access to somewhere. For example, in our case where they can publish poles. So you want that admin interface, so it's ready for you to go in Django whenever you need it. Let's look at how we actually get into that admin interface. The first thing we have to do is to create a superuser, the administrator user for the Django admin. And we're going to do that inside of our command-line switch, going to exit from the Python shells, we have a regular shell. We're going to type clear to give ourselves a bit of space. And then we're gonna go with the classic Python managed up PY. And the command is create super user. So that's gonna go ahead and do that. You can have any username and any password. So I'm going to call this admin. I'm just gonna leave email blank and I'm going to have the password as admin. Obviously. You don't want this to be the case with a production website. You want something that people can't guess. The username being on guessable is, is as important as the password being on guessable because most of the common hacks out there involve testing usernames against passwords or rainbow tables, Hash table attacks. So if your website, for example, is Facebook.com, don't call yourself Facebook admin, right? Maybe call yourself your name, admin, 1-2-3, just something to mix it up a little bit and then password. While I don't need to tell you about that. Complicated ISA, better. But you know, it's even better than a complicated password pass phrases, right? So if you have a sentence, I went to the shops today that is much more difficult to crack. It, pretty much almost impossible. Then it is having a password with lots of uppercase, lowercase commas, et cetera, and the bonuses. A passphrase is easy to remember. So that's enough of that sidetrack. I've just skipped it for this particular tutorial, so I don't forget it. So there we go. I've pressed yes to bypass that validation because it does say, Hey, you're being a bit stupid here. So once we've done that, we can then go over and start our web server. So manage PY run server. Everything should be fine. And we're gonna go to our browser. And the 8 thousand port isn't going to work because we don't have anything there. It does tell us you can go to poles or admin. So we're gonna go to forward slash Admin. And here we are in the Django admin. So let's put admin and admin. Stop asking to save my passwords. And we're in the Django administration area. So in here, obviously we can create groups of people and we can create users. We can also change them. So if you want to go ahead and mess around, you can add, you can give your users permissions. So I'll just zoom back out a bit and we get the name of the user. And there's all kinds of permissions. You can choose them all. You can choose some, you can choose none, whatever you need. And the way, the reason that we have groups versus Users, and we're gonna get into this in a later module when it comes to security, groups is an easy way to assign uses permissions, right? So if I want a group of people that are readers of my website, I want to allow them to comment but nothing else. So I will explicitly allow that. Then when a user is created, I can just give them that group privilege. Okay, so let's go back to home. What's interesting is we have no way to access the poles, right? So let's give ourselves a way to access those poles. Let's go to our poles folder here and admin. So here we're going to register the models for the backend of our app. So I'm just going to copy over some stuff. So we're going to import the question. And then we are going to perhaps put it on here. We are going to register that question modal. So now the admin knows about this database, it knows about the model, and it knows that we can do stuff with it in the admin section. So that's saved the power shell or the, the system rather. We'll have checked that something has changed and it should automatically reload for us. Okay? So if you find your probe, your project isn't working, then just check that command line because it will tell you what's wrong. It's now if we refresh this page, we have questions. So I can click questions. Let's see what we get. Well, we seem to get nothing because I think there's a couple of other things that we have to add in here. Maybe maybe not. Who knows why this is going so slowly. It's running on my machine locally, so there should be any issue. Anyway. That's basically how we register a model with the front end of our website. Have no issues there. So I'm going to pass on this one. Who knows what's going wrong. Anyway, on to the next lecture. 21. Adding More Views and URLs: Now let's add three more pages. We're going to have the vote page, so we can vote for a poll. We're going to have the results page because everyone wants to see results. And we're going to have a detail page that gives us more details, as you might guess. So in our polls slash views dot PY file, here is where we're going to add all these various views. So I'm just going to drop my tests down to here. Sometimes I'll add a comment saying this from here downwards is the test section. But in this case it's quite small app, so I'm not gonna do that. So I'm just going to tab everything over since the Correct tab. And now I'm going to run you through what we've got here. So if you need to copy this down, then pause the video and copy it down word for word. But basically, we have the same pattern that we've seen up here in the index page. If you recall, index page takes requests and returns an HTTP response. Okay? And normally that response, of course would be in HTML, but we're just doing plain texts for simplicity. We're going to get onto HTML in a bit, but not right now. Then we have our Detail page. I'll detail page is where you go into a question and you look at the various details around that question. So this method is going to take both the request and a question ID. And the reason it takes an ID is pretty obvious. We're going to use the id to pull it out from the database later on so that we can display it to the user. I'm going to get into how we plumb in this parameter of question ID because we're going to take that from the URL. And then of course we return an HTTP response, which is just a string that is concatenated, which means smashed together. And we got the variable they represent S, which is assigned to question ID. We've passed in. Now that pattern is pretty much the same for the rest of these. So a results of a vote or a pole is the same idea except we split out the response into its own string and then we're just passing that over. So this is another way that you can do this where you pass in a variable to a string. You can also do it like this in Python. Okay? And then finally we have a vote which is the same pattern, request question ID, and we just return, you're voting on question, blah, blah, whatever it is. Now, obviously, that isn't going to work by itself because we haven't passed in the question ID to any of these URLs. So I'm just going to copy over my URLs here. And I'm going to go to poles URLs. And here we have a whole bunch of patterns. So the blank, the test. And then below that, I'm going to paste in what these examples are, what these URLs are. So let me just separate these out and walk you through them. So feel free to pause right now so you can copy all of these down. Right? So we have a path that looks like this, poles slash five. Now the slash five is the identifier of the particular question, the database identifier. Okay? We know that that's going to be unique because database identifiers are unique by their very design. But what we can do is in the path method here, we can declare that an integer of type question ID is passed in. So if we come, if the URL system in Django comes along and sees poles, when it comes over to URL patterns in polls, it just passes over the five, which is an integer. If this path finds only an integer with nothing else following it, then it's going to assign that integer to question ID. Go to the views detail. So just to remind you, that's this one. And that's just the name of the detail. So that's how we pass over parameters from the URL. We assign it as a type in the path. And then in the views after the request, we take that parameter. Okay? Now it's the same for this path. Paul's slash identifies slash results, except in this one we've added the forward slash results. So if this isn't an exact match, IE, there's some text afterwards, then it will go and search for here do we have results? If so, same idea, pass the parameter to view results down here. Same thing with vote to pass the parameter to views, dock vote, et cetera. And of course, you can have more than one parameter here. So I could have slash vote slash int i j, let's say vote underscore ID. I could do the same, and then I could pass that vote underscore ID into here. Okay, but we're not gonna do that. So let's get rid of it. Controls Zed, we go. Okay, so that's everything that we need. We've added the URLs and the views, and we've passed in the all important variable from the URL. So if we go over to our browser and we go to slash poles, slash one, I don't know why, but my site is not working very fast. Let's try in a new tab. And it's been like this since I recorded the last module. What happens if I just do polls? Well that works. And then it doesn't like you see that spinning icon. Just give me a sec. I'm going to go fix that. Right? I fixed it. You know what I had to do, just restart the server. But when I restart it, when I killed it rather look at all this mess that I got of all the errors. Now this isn't an era in my code. This is something to do with Django core that I can see here said lesson for you is if in doubt, restart the server. So right now we have everything working as it should. Our server is running. So I can go back to what we were doing. If I put poles slash one, I get you're looking at question one. If I put eight, you're looking at Question eight. What would the other things we have eight slash results. It's always a good idea to check that you're actually loading the views you think you are before you get more complex than this. And then we have out you're voting on question eight. Okay, that's it for adding those three views. 22. Writing More Realistic Views that do Something: Now our index pages a little bit boring because it doesn't give us an index of all of the polls. So let's fix that. Let's head over to our code and let's go to the views under the poles directory. In fact, usually when we're discussing stuff, I'm talking about editing the files in the particular app that we're discussing in this case, poles. So if it's evidence show default to that 1 first, what we're gonna do is change this and I'm going to drop in the code that I've already got here, which never drops in indented correctly, does it? Ok, so the first thing is we have to import our model, which is the question model from the models folder or not from the models folder, rather from the models file. Okay, so that's the syntax for that. Then I've changed my index request to do the following. I want a list of questions that I am going to pull out from the database questioned on objects, and we're going to order it by the pub date negative pub date means descending, so the latest 1 first, and we're going to limit that to the amount of five total. Whenever you're running a web service of some sort, you want to be careful with arrays and pulling out data. You never want to send too much data to the end user. So in this case, five is probably too little. I would have probably done ten if it was a production Apple, something similar to that. But it also depends on how you use, it uses the site. Do they load up a list of votes, open one vote, and then leave. In that case, you don't actually want to send over too much data. If they do 20 votes, you might want to send over all 20 and just keep them cached on the client side to save your server being hit that many times in as many seconds, probably. Ok, so that's just a little aside for you there. And then we have an output which is basically concatenation, concatenating the arrays properties, and just putting a comma in there, you'll see what that looks like in a minute. And then finally, because Django expects an HTTP response or an exception and error, we're gonna give it an HTTP response. So let's save that and let's reload our index page. And now we have what's up. So we don't have a full list of items. Obviously, we're all we've done is loop through an array and say, hey, go show me the question texts in that array. In the next section, we are going to look at why this is an awful idea. Can you guess why before I say in the next couple of seconds? It's because we are hard coding, a view, hard coding the HTML or the template. If I need to change this in the future, I can't just change in HTML file. I need to change code. And that's much more tricky than just changing plain old templates. So in the next section, we're going to look at templating this to make it more usable and to separate our concerns better. 23. The Templating System in Django: Previously, I said that it's a really bad idea to hard code our views. So let's fix that using the Django templating system under the poles directory, we're going to create a new folder, which we will call templates. And now most people would just whack their templates under their thinking that the poles directory, in combination with the templates directory is unique enough so Django doesn't get confused. But according to Django zone documentation, and I can't quite believe this is the case. If you have a template in different apps or Templates folder, Django will just go and match the very first template that it finds regardless of which parent folder it's in. So you can see the danger of that. You'll get some template that you didn't mean to get. So what we do is we apply namespace into this. So in the templates folder, we create yet another folder called poles. Now, I don't know if what I'm about to say is actually correct, but I assume that Django collects all the templates together and just puts them all in one template directory with the subdirectories inside of there. If you've created that subdirectory, Maybe that's the root of that problem. I don't really know. Anyway, inside of this pose directory, we're going to create a new file, which is the index.html. And inside of here, I'm going to simply paste some code and then we're going to run through it. So if we have a variable available to this view code, the latest question list, then we're going to create a list. And then we're going to say for each question in the latest question list, we are going to create a bullet point with a link, links to a, another URL. And that URL is made up of poles slash question ID. Remember that's the detail view that we've done before. And then over here we've got the question Doc question texts. Notice that these double curly braces mean we are taking a variable from the parent that is feeding this view, okay, and it's the same actually an angular. Forget the exact technical term for it. I'm sure someone can write in the comments what it is. And once we've done that, we're going to end that for loop. And then finally, if those latest questions are not available, we're going to have an else that says simply no poles are available. Okay? So we can save that. And of course that's not going to load yet because we haven't set up any of this stuff. What we have to do is go back to our poll slash views and change this index. So the first thing we need to do is import something which is the following. A loader. Now what is the load or do? The loader makes it simple to load templates basically. And that's best seen by replacing this index request with the following code. So pause the video if you need to copy this now. Right? So with this code there, we have that variable that we referenced in the index.html, right? Which simply collects the objects ordered by pub date descending maximum of five. Then the template is fetched by the Loder from that particular directory. Now this isn't the root poles directory because remember we have poles templates, Poles, we have three directories. This is simply looking in the template directory for the Polls app, right? So that is looking here. We have to tell it there is an extra directory you have to drill into to actually find that index.html. Okay, and then finally, the context defibrillators question is latest question list. So that's how we're actually passing it over. Our context is the Africa, the tech term for it. But it's basically the scope of what we're running in, which allows us to pass that over. And then we return an HTTP response with the following special method. We have to render the template using the context, and that context passes over those variables and pass over the original request. Okay, so if we save that, go over to polls and reload this page. Now we have a list, right? So that is now pulled data from the database and shown it in the list. And no matter how many results we show, it'll just expand the list because of that four loop. If we click the URL, it says you're looking at question one and notice how the address has changed in the top bar. So that is a basically how we use the templating system in Django. 24. Render Shortcut for Templating: Here's just a handy time-saving tips and is also how you're going to see a lot of Django projects written out there on the web. This is a very common task, gets some data loaded template, pass the data into a context, and then return HTTP response with the rendering of the template. Luckily, jangle makes this easier for us. We're going to import the following line. So Django dot shortcuts are very useful class we're going to import render. And then if I just copy this index change here, we're going to leave the latest question list, but we're going to cut out the template because we no longer need that. And instead we're going to do the following. We're going to render that's coming from the shortcuts class, the request. And we just pass in where the template currently resides, as well as the context. So we don't actually need that loader anymore from templates because the renderer class is doing all of that business for us. We also don't need HTTP response, but we are currently using it down here. So I'm going to leave that in there for now. As it turns out, we do have shortcuts at the top here. I didn't realize we already had that in. Anyhow. That's the magic line. You need Django shortcuts, render. And of course, as always, we should refresh the page and everything works fine. So whenever you make a change to your code, always check that the section in question hasn't been affected negatively. That's something we'll actually cover when it comes to testing in this course. 25. Raising a 404 Page Not Found: What if you go to a poles page so we know we have question one, but let's say we go to poll eight. We know there is no ID with eight, and we know our template is lying to us. There is no question eight. So if that happens on a live website, you want to give the user what's called a 404 page four or four is just the HTTP status code for resource not found. Okay. And you've seen that before, definitely on the inset. So how do we go about doing that? Well, luckily, jangle makes it easy for us. The first thing that we're going to import on our poles URL polls views page is the fluorophore class from the HTTP class, right? And then down here in detail, what we want to do is say, hey, if you can't find this particular question in the database, then I want you to return the fluorophore. So that's quite involved. I'm just going to simply copy the code as usual. You can pause and copy and write it out. So we have our detail. This section is the same. We pass in the question ID. And then we're going to do if you're a.NET developer, the equivalent of try-catch, same in many other languages with the try-catch syntax, we're going to try and get the question with the primary key of question ID. If that fails, then we're going to have an exception down here. So, you know, it's not worked. The question does not exist, gets returned, and were going to raise an HTTP 404, which is what we've imported up at the top. And just send back some plain text to the user saying question does not exist. Otherwise, if this doesn't stop the execution of the method, we are going to fall through to this section, which is our classic render request, polls, details or HTML that we haven't created yet. And the context. So instead of creating a context object, we can just pass it in like this. So that is equivalent to that. We don't have to name the variable. It can be totally anonymous, right? So we need a detailed dot HTML page in the polls Templates folder. And this is just going to be a place holder for question. So recall that we've passed in V, Where are we? Down here? We've passed in the questionnaire. And because we have that string method in the question, not that one, but this one. It's going to return the text when this happens. Okay, so we're back in the views now. Let's go to our page. So poles one works fine. If I try poles to page not found, 404 question does not exist. Okay. And it says you're seeing this error because you have debug equals true. Obviously when you're in production you want attend debug off, so that'll be false. Change that false. And daga, daga Django will display a standard for O4. Page Okay. Now just looking at my notes, I added a little section here saying it's very common idiom to use get and raise the HTTP 404 because you do it in a lot of places. And of course, there is a shortcut. So from our shortcuts we are going to paste the following. So we're going to import render. And we're gonna import Get object or 404, which means we should no longer need this once we've changed this detail. And what I'm gonna do is the following. Sometimes I almost feel bad that I'm making a copy out code. And then two minutes later I deleted all. Hey, that's part of learning, I had to do it. So the question is going to use this get object or for, or for. If the object is no, then it's going to give us a 404 automatically, right? If it's not, then it will fall through to here and do what it needs to do. So compare that to what we had previously, which was that. That's a lot of code. But I prefer this code obviously because it's easier and it's flatter and it is simpler to understand. Let's double-check that at posed to refresh. Can't reach this page. That's very interesting. What do you think the problem is here? Did I save my view? Yes, I did. Why not have this set as homework? What do you think the answer is? I'm not going to do a separate lecture. I want you to pause the video now and if you're having this issue, then figure out what the problem is and fix it. So pause now and I'll see you in a minute for the next solution. Now I'm guessing this is the solution that you didn't have this problem. Do you know why I had this problem? Excuse me. It's because I copied and pasted in code from a plain text editor. And if you double-click this empty space here, you'll see all these dots that represents a tab. Look what happens if the one below it. I have this little dash. So there's some spaces lurking in here, right? Whereas above we have this weird space slash tab. So what I'm gonna do is select all of this press tab, select all this press Tab, and save it. Now, if you go and look at your command line previously when you saw the error happened to me. It says we had an error on there, which is inconsistent use of tabs and spaces cell. Of course, Python is pretty fuzzy when it comes to tabs indentation spaces for good reason. And once you get it right, your system should automatically reload. And now if we go to poles slash two, I still get nothing. Let's go back one, it's got two poles, one and see if that works. That does. Let's go to the polls to nothing. I don't know this server is a bit Jackie to me sometimes. So let's run that server again. And let's go to poles to. There we go. So now it works. That's why you should use the server in production. It's bad enough in development. Anyway, we have no question matches the given query. And you'll notice from our views, we didn't actually say that, did we? So that is a standard django response, uses the model name and then this is its template in the background. Okay, that's that for a weird bug. And for more things on 404. 26. Task Creating a Template for the Detail View: Here's a little task I would like you to create a template for this detail view. Okay, so if we just look at our current detail object, we have a question there. What we would like in our detail view is something more than just the text of the question. I would like the question, and I would like the number of choices or the choices listed out for each question. You remember when we were messing with the database in the shell, we added some choices to the question. You can access those choices by accessing question dot, choice, underscore, set, dot all. That's how you retrieve it from your detail view. So you're going to have to do a little bit of messing around in the detail view. So what we're gonna do is paste in the following code. So this section is going to contain the question title. This section is going to contain choice. And up here you will have the for loop for the choices. Now this is based roughly on what we've done here. So there's no FL's. In this case. There is a for-loop though. So I want you to think carefully about how you do it here and then apply that logic to here, except using the question, where is the question? Dot choice underscore set. Okay, so that's your homework. Give that a good go before you look at the answer. 27. Solution Creating a Template for the Detail View: Right, so let's have in the one section the question dot question texts. So we've interpolated that was the word I was looking for earlier, interpolation into play to that value into our template. And then in the URL section, we have the for loop that we want to create. So for each choice in question dot choice-set dot o, we are going to do the following. We are going to interpolate in the choice dot choice underscore text. And then once we finish that, we are going to end the for loop. Like so. It's a really fast going to indent these correctly, or to my liking, it would be like that. Ok, so whenever we have curly brace percent, we are executing a piece of code. Whenever we just have double curly brace, we are interpolating a variable into our template. So we can say that we can check our PowerShell. That looks alright, no air is there. And then we can refresh this detail page. Now we have the h one and all of the choices looped through. So that might have been a bit challenging, not this section, but probably this section was a little bit challenging. I hope you got there. If you didn't delete the template now and try and rewrite it as a sort of second homework. You really learn by doing things twice. I've learned, I've learned that the hard way actually. Anyway, that's the end of this one. 28. Removing Hardcoded URLs Better Design of Our Code: So there's something wrong with our index.html. Can you guess what it is? Well, if you've been a developer for any length of time with his weight and salts or gold. Then you'll see that we've hard coded in a URL into here. What if I want to change that URL to my poles or all poles? While I'm going to have to change it in a few places, aren't I? That's a bit painful. I have to change it here. In fact, I'll have to change it in all of the templates in the poll section. And of course I have to change it in the master URL patterns. But the master URL patterns change is fine. That's the way it's designed. It wasn't designed. That's Django to do this. So what we're gonna do is change this H ref to the following. Right? So we have basically the same, except now we are doing things slightly differently when it comes to the URL. Or we're gonna do instead is pick up the URL from the URLs dot PY. And how do we know we're going to pick it up or which one to pick up where we have the name, which is detail. And if you recall, we set a name in nice and that's one of the reasons that we have a name. And then of course we pass over the question Id. Okay, so that should work perfectly fine. Let's refresh that. Let's go back here, refresh that. We are all good. So now we don't have to come in here and change the poles URL. If we change it elsewhere, it's automatically reflected using this system that Django has come up with. 29. Namespacing URL Names in Django Important: You guys are going to love this. There's another problem. The problem is this detail. How does this template know which detailed to go and look at? Well, you'd think it's just the name for the URL patterns for poles. But actually Django doesn't know what the Polls app is called. It just knows a bunch of directories and files at the moment. So what we're gonna do is add the following line into the URLs dot PY. So just up here at the top, we're going to give this a name of poles. And what we're effectively doing is namespaces stuff. We're having a collection of functions, classes, objects, whatever it is, all classed under a named space. So because it's a namespace, we have to be very careful. You can't obviously have the same namespace or we're gonna get clashes, might get clashes between things and very weird bugs that you don't expect. Okay, so we've given this namespace, we now need to specify that in the index.html. So instead of detail, we're actually going to have the following polls, colon detail. And that's pretty much the only change we need to make. So now it knows go look in this namespace and find that detail URL. So now if I go back, refresh that page it works. Go to the next page, refresh it. It works. Ok. So that's pretty much that really always namespace your apps in the URLs dot p, y. 30. Creating an HTML Form: Now we're going to look at forms, in particular, a way to submit poll. So if I'm an end user, I can click something on a pole and it will vote when I press the vote button. And to do that, of course, a form is the logical choice. So what we're going to change is the template for this detail view. And if you recall, that's in templates polls detail. So at the moment, we just have a placeholder in there. What we're gonna do is actually have a template that looks a little something like this. So pause the video if you need to copy this down. We have the same question. Text is a heading. We also have this block that allows us to show an error message if it exists. Now we're not actually going to implement that in this section or in any sections really. But it's just so I can show you the kind of things that you need to be aware of when you're making a production app because you will have errors and you do need a way to show the end user, and this is one way of doing it. Ok. So one constraint on the error messages too much, but you can look those up online, they're pretty easy to find. Now, we have a form here which has an action, right? So this is just bog standard HTML. But that action takes this django syntax where we specify a URL, which is polls, vote, and a question ID, right? So we've got all the elements that we need to define, what the URL is and what the idea of the question is. And the method when this form is submitted is post because we're posting some data to the server. Now this is the cross-site request forgery token. If you just look up cross-site request forgery is on Google, you'll find out everything you need to know about why we need things like this. Csrf fs basically mean that people can't hack our website, a different website. It's a very simplistic view, but this isn't the place to go into things like this. But whenever you have a form, make sure you have this. At least in Django, other systems have the same thing, but obviously not the same syntax. And we go down to the next line where we say for each choice in the question choice set. So this is the standard for-loop that we've seen so far, which ends down here on the end four. We're going to do the following. We have an input type, which is a radio button, as in you can select an option. The name of that input is choice I. Either choice you're going to make. The id of the input is the choice and the for loop counter. So there'll be choice 0, Choice1, Choice2, et cetera. The value of it is the choice dot id. And then we can use that ID to store that vote in our server, right? So we've got every element we need in the input. And then we have the labels for the choices. Because obviously a choice with an integer or something like that, oh, no text is useless to the end users. So we have to set some labels, which is the same idea for each choice, 012. We're going to show the choice texts. And then finally we have an input type of submit at a value of vote. Okay, so we're gonna save that. And let's go to the polls. Click that. And there we have it. So we have not much the sky and just hacking. And you'll notice because we have this radio input by default, it only allows us to select one in the form. You can of course change that if you want to allow someone to select more than one. So I'm gonna click this guy and I'm gonna hit vote. And then it tells me you're voting on question one. Obviously that's something we need to change, but we have now voted on that. Okay, so that's what that form looks like. The background. Now if you wanna get really complex, you can right-click and click inspect. This is running in Internet Explorer, which has come on leaps and bounds or Edge or whatever they call it these days. So that we actually have something that looks very similar to Chrome. I'd advise you actually debug your apps in Chrome because Chrome has all the tools that you need. But for a simple course like this, this is fine for this use case. So if we look at the Network section here and I go to WhatsApp, it tells us that we retrieved this page one, that is whatsapp. And if I click just hacking again and vote, it tells us we retrieved this page. Vote. Now you can dig in there to see what your requests are doing and go further than that. But if you didn't know about this tool where you can right-click inspect. I just wanted to show you that it exists. Feel free to click around everything in there. You can't really break anything by clicking around. Well, my famous last words you might, but anyway, it's a handy tool for you to understand as a new web developer, if that's what you are, what's going on, the most useful thing I normally find is this timeline up here, which also shows me the waterfall. So when you get stuff from a page, you'll get say the index.html, then you get the CSS, then you're gonna get the JavaScript. This will show you the steps for each or how long each step took in them, and that forms how long your request takes in total. So reducing that time is actually very good for your user experience. 31. Creating a View to do Something with Submitted Forms: Right, so we're able to click vote and we hit this input button, submit our vote, and nothing really happens, or we have no way of telling the user what happens, even if something is happening in the background. So what we're gonna do is fix that problem. And we're going to do that primarily in the polls slash views. First, I'm going to look at the polls slash URLs dot p, y. And what do we got here? We have this view looking at my notes is that one where we pass over the question ID and the slash vote. So we're going to work around this particular URL. So going back to our views, we're going to totally replace v vote here. So I'm just going to copy a lot of code over. We're actually, I'm going to copy some inputs. First, says copy this and go up to the top. And over here I'm going to drop these in, just get rid of that because that's now covered by the line below. And get rid of that, right? So you have an HTTP response like we did before. And we have an HTTP response, redirect. It's pretty self-explanatory what that does. We also have this which is reverse. Now in Django, you have usually a URL. And from the URL, Django knows which view to call, right? But sometimes, or fairly often actually, you have a view, but you don't know the URL for it is a reverse, allows you to get that URL. So hence, reverse, it's going instead from URL to view, front view to URL in the reverse direction. So that's a concepts you'll see popping up fairly often in Jagger, right? So now for the vote section, copy this, so I'm just going to replace it. The signature of the function is still the same. So don't worry about that. And let's tab all of this stuff correctly. That should do it. Okay, so what have we changed here? Well, we have the question which is get object or for or for. I think we had that before, did we not? Yeah. Then we're going to try something. And it's important to have a try statement because we don't know if when the user clicks vote and that post request goes off, It's actually going to return something, my return an exception and error. And so obviously you want to handle that gracefully rather than just crashing be application. So that's why we use try in here. And if we have an exception, we are going to put the following in with the error message. You didn't select a choice or while in this particular case, that's probably the only error there is. There also be errors around the network connection they might want to deal with, but that's sort of a complicated subject. What's not complicated, but it's an advanced subject. Something to think about later on. Okay, so if we have that selected choice, you can see down here, we're going to add one to the votes and then we're going to save that selected choice. So all of that is actually running in the database for us. This is one of the great things about Django is we don't have to do all this database manipulation manually, like you do in many, many other systems. We need plug-ins for databases. It's all included in Django. And it's very simple and straight forward. Asynchronous versus synchronous. That's a whole different question. And we can cover that in a different course, but not in this one. So if you're an advanced developer just learning Django, yes, of course we can't do asynchronous, but just have a search on Google for that one to see how these things change when you talk about async. Anyway. So these are the notes from the Django dogs always return an HTTP response direct After successfully dealing with post data. This prevents data from being posted twice if a user hits the Back button. So that's a common error pattern. Press the back button and that gets resubmitted. You'll see that on browsers when you press back and it says, do you want to resubmit this data? Okay, so that's what HTTP response redirect actually fixes for us. And then we see this method of reverse coming here. So it says if I've got a poles results page with arguments of question ID, then give me the URL. Okay, so that's what it does, and let's get rid of this space here. So that is actually pretty simple. I mean, if I remove this stuff here, like so, it's not a lot of lines of code to get what's a fairly complex chain of events to happen and to send a response to the end-user. So that should now work in our poles. Let's go into a poll, click not much, and hit vote. And we are looking at the results of question one, right? So we don't actually have any results to display to the end user at this point. But that pole should have gone in to the database. So if you ran into the database here, you could double-check that that was actually being implemented, that votes plus one. We won't do that, or rather, you can do that for a task. I won't give you the solution. You might have to go back a few lectures to see how to use the python shell with the managed PY, but it's fairly straightforward to see what we have in our database. All right, onto the next section. 32. Directing to the Results Page: Now what we're gonna do is set up our results page will have a click of a, it goes to your generic results page that we haven't actually set up yet. So the first thing we're gonna do before we create the template is adjust the view in polls. They impose slash views dot p, y. So we have all the requirements we need. We have get object four or four and render. And so I'm going to copy over the code for our results and you'll be pleased to know that this is pretty simple. So at the moment we have a simple text response. Obviously, we don't want that. We want something with some index, some HTML rather. So it's the same signature we pass in a request and a question ID. We get the particular question with the primary key of ID, and then we're going to return that render with the request passed in. And we're gonna get this results to HTML that we're about to create. And we pass into that results to HTML, the context which has a question variable that we assign to question. Okay, so that is pretty much the same as the detail up here that we created previously. So let's create in template slash poles. The new file, which is results dot HTML, not results. Fun fact spelling is the biggest source of error in web development. I know from experience. So I'm gonna paste in what we've got here for results. So we have the question text. That's pretty straightforward. And then we have a list, which is for each choice in the question dot choice set. So that question is what we passed over here. Let's actually save that file. And then we're gonna give the user a list of the choice texts. And we're also going to tell them what the votes where for each one. Now, this is very, very useful. We have vote. A very common problem is singular, plural in any sort of programming. So you always want to show your user, say, 12 votes or one vote. And usually what ends up happening, barring someone developing a plug-in for this, for whatever system you're using is that you have to create the logic yourself. To make it singular or plural. You have to say, if this is below the count of two, then it would be vote, else it would be votes. Thankfully, django makes this simple for us, so we have vote. And then what this does is insert a pipe here that says, hey, if, if it's multiple, then pluralize it. If it's not, then don't write. And then we're going to end that for loop. And then down below, we have a link to the vote again, so we can allow the user to vote twice. Not sure why you'd want to. This isn't the American or the action K. So let's go into our pal. Let's say not much and vote. There, we have it. So there's our list in action to vote zeros 0. So if I go to vote again and hit not much, we should now get three votes, which we do. Okay, so that's that for creating a results page. It's pretty much all the same stuff you've seen so far. We have a for loop. We're just getting the values from that, from each variable we created an a for loop. And the only thing that we've added here is really an H ref I link to vote again, the results page is very straight forward. It's a Django, as you can see, provides very good separation between code and the HTML. 33. Generic Views to Avoid Repeating Code: If you're a developer worth your salt, then you're starting to look at this and go. We're sort of repeating a lot of stuff here. I'm not sure we should be repeating. Whereas the dry concept and for those of you that don't know, that's do not repeat yourself. It also has some other far more rude meanings which mean the same thing. Because developers can get quite rude about repeated code. Ok, so the solution to this is to use generic views in Jenkins. And we're going to start the generic views by looking at the polls URLs dot P, Y. And so we need to change some things in here to use generic views. Now, you are going to use generic views in the way I'm going to show in pretty much all of your apps. What I've shown you so far was really to get you to understand, or rather it was Django docs that pointed me in this direction to they're trying to get you to understand how the whole URL templating system works under the surface so that when you run into trouble later, because of that understanding, you can solve those problems. So generic views, what I'm about to show you is what you will actually be using in production, right? So what we have to do is convert some of the views here. So we have vote, we have results, these things have very, very similar. We have app name that stays the same. So I'm just gonna look at my notes here. I'm going to copy these over sort of one by one. So first of all, we're going to look at this index. And I'm gonna paste this in. And what I'm actually gonna do is paste it in below and then delete the first one. So this is the old one up top, and this is the new one down below. So we have these views dot index view. Now we haven't created that because in our views file, it's just index, but we will create that index view soon enough. We'll get onto that. I will explain more than that right now. And of course we going to have it as a view. And we're going to have a name. And the name is still going to be the same index. Ok, so that's literally the only thing that's changed in that line. Now it's going to be the same for, let me copy this code detail, results and vote. So there's the old stuff up top. And I'm just going to make some rooms you can compare. Okay? So what's changed here? Well it, up here we have an integer of question ID. But with the generic views, we're actually going to pass in a primary key, okay? We're just changing the name of that variable. And then it's the same pattern we saw in the index views dot detail view. We haven't created that yet. But the eagle-eyed among you will notice that is a capital D. So we actually going to create a class for the view rather than a function. The name of course, stays the same and we have the same pattern for results and for vote, but voters actually stayed the same. We're not doing it for the vote. So I should probably just make that pretty clear there. Ok, so all we've changed the use generic views of these to the detail view and the results view, the vote. Because that's a form that's a bit different. Yeah, we could use generic views for it, but we'll leave it as it is for now. Okay, so that's all we've changed is that that and that, and that we still have our TextView from earlier. Now we go into the views dot p, y, and we're going to amend the detail and results as well as the index or index detailed results. It's this section will modify. First of all, we need to have the following from Django dot. That's the thing. Import generic statured auto-fill for you. So generic views is what we're going to use. So let's first look at Max. You're gonna do this backwards and limited results view first because it's a shortest stub of code set. Let's get this all lined up nicely. So we've moved from a results to a class view. And because we're passing in a variable, it's just called engineering views of the model, right? So that's syntax. You'll see in pretty much every programming language, the model. What is it? We're going to say that is the question. And then we just pass in the template name. So that's literally everything Django needs to get going on this. So we can remove this results view up here. We don't need to do the 404, we don't need to do the manual database query. So just lining that up so you understand it. It is there. So this primary key is passed in because the Django generic views knows that we're going to get something from the database and we need a primary key for it, hence PK. So that will be the question Id that gets passed in. And then the views dot PY knows that whatever is passed out from the database is going to be assigned to the model, which is of type question. Okay, now, as there's quite a lot going on here. And I was gonna say if you type stuff, you get handy hints, but not in this particular case. Okay, so now we're gonna do the same to our detail view. I mean, really this is something you don't particularly need to understand that well. You just need to know how it's implemented. So the detail view, look at that. It's exactly the same model equals question and we just pass in a different section, a different template for it to render. Straightforward. Right now time for the index view. So this index view I'm going to paste below here so we can see the differences. And so I've got some errors out. It's just the syntax that we get, or rather the indentation. So previously in the index we get the question list from the database manually. We do the context and we render what's left, right. Now we've changed to he has your template. Here's your context object name, right? So instead of model, we're saying, we're actually going to give you a name for this. And the name for it is this. And so that corresponds to doing what we've done up here in these 34 lines, right? And then we have to define get query set. So in this particular case, we don't just want the system to go and get random stuff from the database. We want a bit more control. And so we're going to return the last five published questions. And so that query is there, which matches what we had before, except we have fewer lines of code here and we using the generic views. So let's remove that index view and save everything. Okay, so I hope this is coming through pretty clearly. Are, I'm not that great at explaining this because I don't think any developer has ever dive deeply into generic fuse. That they're just generic so that we don't have to do a lot of manual work, which is why we don't dive deeply into them. Okay, so that's the generic view. I did say earlier that you will use mostly generic views if you structure your app, right? It depends on what kind of developer you are. Anyway, let's go back to our poles. So it should have reloaded. We can check everything's fine by going to the PowerShell. Yep. Let's keep going back. So we have our policy should refresh the page. What's up? Just hacking vote that has been passed through. So you don't see any difference. But you feel a difference because you have much less code to write with these generic views. So I hope that was fairly understandable. And when in doubt, go with a generic view for your views. And only resort to these custom views. Custom views when the generics don't satisfy what you need to do. 34. We Found a Bug: Let's discuss something that is going to happen to you over and over again for the rest of your professional development life. And that's something is called bug fixing. So I used to be off the mind. I used to wonder why people ran tests in their, in their software. Because, you know, once you get used to a certain framework language, you get used to fixing problems, diagnosing problems. And I never really believed that testing could help allay that unless you're in a huge team, in which case it does make sense. But I've come more and more round to the view of that writing automated testing. Now, it's actually going to save you quite a lot of time in the future. So call me you convert if you like. Now if you are new to coding, you're probably wondering what testing is. Well, before we go down the route of finding bugs and testing them, let's say we have the following function vote. We put something in to the function and we get something back from the function which is there and in this case there as well. When we write a test, we say call this function, drops something in it. And let's see what we get back. If the things that we get back our what we expect or of the type that we expect, then we say the item has passed the test, okay? So there's actually a bug in our application. And if we do the following, so we're going to do, first of all, from our shell, we're going to find the bug here, but managed p shell, right, so we're in the Python console. The first thing we need to import the datetime because we're gonna do stuff around datetime. And then the next stuff we're gonna import, I'm just going to copy it because it's faster. Again, feel free to pause if you want to follow along from Django utils, we import the time zone. And finally 0. Second last we're going to import from poles dot model's going to import the question because this is actually where our bug lies. Now we're gonna create a new question that sits in our database. And we're going to call this future underscore question Is equal to new question with the pub date of the time zone now as the current date-time according to the system that we're running on. And then because we're using this datetime, we can just say, I want you to add or subtract a time delta, so it difference in time of 30 days. This is going to set the date 30 days in the future. It's like I'm publishing something is due to come online and appear in the website in 30 days time. This is not an uncommon task that you'll need to do. So when I present on that, that is now in the set in the database. Remember that we have this method for questions. Whereas the in models that is was published recently, that says if the published date is within one day of today's date, then we will return yes, for this and of course we use was published recently to display a list of the most recent items. But if I call that on the future question. It says true will clearly, this is due to publish in the future. So it's not actually published. That means naturally that we have a bug in our method here. This is not the correct function that we need to define for was published recently. Now, of course, we can fix the function right here. Right now. We can be more diligent software engineers and think of all these edge cases are not even edge cases, just cases where the function return would be wrong. But there's a better way of doing it, like I've alluded to at the start of this lecture, which is we can write a test to assert that was published recently, is actually turning something, returning something, correct. Okay, so Python automatically knows about testing. If we have a test.py file. So I haven't created it yet in the Polls app, we're going to right-click New File and get tests. Dot P Y at says I already have that. Yes, I do. Here it is. Test.py. So this is also created in this case. And it tells us we can create our tests here. Now of course, we can specify somewhere else to put our tests using the settings for the overall application, but we'll leave things as they are. So it's best to leave things as stock and work with stock, right? So I'm going to copy paste a whole lot of stuff into here. And just start from the beginning paste. So just like before, obviously we want datetime because we're going to write a test around that published recently. And I'm just aligning all of the stuff. We're going to import this special class test case and of course times n. So because I'm one of those developers, I like to have the like stuff sitting next to each other. So when I look at my inputs, I can see that I'm doing two things for date times here. We have test case and of course we import the question. It's never create a class that relates to the question model tests. And the test name is something. This is quite verbose, but hey, this comes from pipe from Django themselves. So blessed, just use it. Test was published recently with future question. Okay. So this is just the text they've included was published recently, returns false for questions which pub date is in the future. So we'll just delete that because we should be able to discern that from the test itself. So we're gonna get the time with a time delta in the future of plus 30 days. We're going to create that future questions. This is just the automated steps of what we created before. And then what we're going to do is perform an assertion that the future question was published recently comes out as false. So what we're gonna do here is when we run the test on this function, that should return false when we pass in this time. Now, as you might have guessed, that is going to return actually true. So this assertion will fail, and that's the core of the test, right? We're saying this should return false, but it is, but we know it's going to return true. Okay, now we can run our test in the terminal. So here we are. We're going to exit out of this. And we're going to run it right here. So Python manage PY test poles and the Python system knows where to find the poles tests. So it just runs through that whole file. And we are going to get the following, creating a test database. So this doesn't pollute as far as I'm aware, your existing database, it does its own thing on the side. So no issues. But it tells us this Fail was recorded on the test was published recently. A future question. Ok. And what it does is say this particular line, remember in our test file where I said this is the core of the test, true is not false. So now we know that we have a failure in one of our tests, as in we have some bad code. So obviously we can fix that and that's what we'll do next time. 35. Fixing the Bug and Testing Again: Right, so it is time to fix this was published recently because our test is failing. So what we're going to first of all do is have a time zone now method. So we get to the current date-time. And then we are going to return the following line, which I'm just going to copy. So we're going to have now subtract one day less than or equal to the pub date of our question, but that is less than or equal to now, sir, actually constraining this between two variables, one day in the past and the current day, datetime. Ok, so that at a really simple change. But if we save that and we come up here, we can do run our test. It will make that database and then we'll see that the test ran in whatever seconds and everything was absolutely fine. Now let us do a task. Wasn't gonna give you a tough initially for this section, but I think you are actually capable of doing this. So I am going to copy some, some stubs of code over into our poles slash test.py. I want you to think about creating the following two tests. So there is this one and there is this one. And their names of course, should be self-explanatory. So we're going to test if something was published recently with an old question, I eat many days in the past, more than one day. And test was published recently with a recent question As in where question that is newer than one day ago but still older than the current date time. Okay. So I want you just to have a go at creating these tests. It's more or less the same as this, just of course, with some slight changes. So good luck with that. 36. Task Solution Adding Comprehensive Tests for Dates: So how was that little bit of thinking required but not too much thinking? I'm going to paste in the solution here. So what we have is, again, we're going to set that time for was published recently with an old question as time zone now and we're going to have a subtract one day and 1 second because that should fall outside of the one-day criteria. We're going to have an old question where we set the date, time for the publishing as that minus1 day in 1 second. Then we're going to assert that the old question was published recently. Okay? So of course we can run that. But first I just need to comment this out or cut it out just to make sure nothing breaks on this. Well, it won't compile if, if we didn't do that. Okay, so wherever here we are going to run the test poles. So this now has run two tests, ok, and two tests have passed. So that sounds good to me. Now we need a test that was published recently with a recent question. And let's again copy paste. Let's say it's the same idea. We have the time is, the times are now plus datetime delta. So what I might start doing with this is because look at this, we have lots of lines of code where we're setting the time. I might, you know, if, if I was doing a production app here, I would say get date, time, something like that. And then like the delta in seconds, whatever it is and I'd return it. But seeing as we're just learning here, I'm not actually going to go that far, but it's just things to think about as you create more apps. So anyway, here's our time where we add the datetime in the future. We create that future question. And then of course we do the same assertion. So we can save that. And you'll notice that this assertion is always the same, right? We're asserting just that particular method. So again, we can run all of those tests in one go. And it says we ran three tests in 0.002. So now we've covered the three test cases. We have an old question. We have a question that's no older than one day and we have a future question and all of them return that correct assertion. Okay. So that is all sorted. It's weight in my notes, I've got something that says test fails and that's good. But I have had no failure of the test. And that's probably because we've fixed the model. It's funny how you can get caught up in your own code and writing notes for things that don't actually exist. Anyway, if your test fails, that's actually very good because you've caught a bug before. It becomes a bug that you've got to search for in production in the worst case. Anyway, let's move on. 37. Testing a View The Django Client Makes it Easy: Oh, okay, we're gonna do some testing of our views, but we're only gonna do this in the client will actually going to write some tests in our code base. You of course, can do things however you wish. So first of all, we're going to go into our Python manage Shell server in our shell. And we're going to start doing things manually in here. So what I'm going to import first of all is the following. So from the test utilities in Django, we're going to import the setup test environment. So once that's imported, we can actually then run it so we can just copy that. Whoops, I pressed Control X instead of Control C. And then we of course run it by typing the brackets in. And then the next thing we need to do is import the test client. So from Django dot test import client. And then of course we have to set up the client so it's client is equal to a new client, right? So we have the client and we've set up the test environment. Now, we should be able to get a response from our application. So if I do the following, man, this, this copy pasting is doing something funny. I think my screen recording software is doing weird things. So we're trying to get a response here from a, just a top level domain. And of course that's not going to be found because that's how we've designed our particular application. Okay, now, if we get into the response status code and represented on that, it'll tell us 404, which means not found. Okay, so we can start debugging our app and seeing what's going on just by interacting with the command line. So, in a way, this is also a test environment, although we're not doing the tests like we did before with the business logic up here. I'm just showing you a different way to do the tests. Okay? What if we have a URL whereby we know we're gonna get a correct response. So what we're gonna do first of all is import reverse. And remember I said reverse is going from the view to the URL. We're just doing that for convenience right now and you'll see why when I post the next thing. We are going to have a new response is equal to. So if I call response here, it's going to overwrite what's up here. Response is equal to the client we created gets the reverse of the poles index. So that's actually going to call the poles URL. And so that works fine. We don't get any response from that. But we can ask it what the status code was, which is two hundred two hundred and HTTP status codes means everything's okay. We have some stuff. You can, of course go and look at response dot content. And the contents returns is, as you can see there. So that's the body of the HTML that it is returning. I. Okay, we can also ask the response, what's the context variable, latest question list. And it will tell us this is a query set array of questions. Ok, that's, it's basically telling us what that variable isn't spitting it out for us. So this is another way of testing and debugging your environment. Of course, I think most people will go to their actual application, right-click, click, inspect, and then just look at sources network. All of those good things in Chrome, I know this is edge, but edge does the same thing more or less these days. And we can see what all of this stuff does. We can watch the request waterfalls. But this lecture is just to show you that we can do things in the command line using the client and setting up the test environment so that we can go through these URLs and see what the responses are, the fluorophores that to hundreds, etcetera. Ok, so we can move on now and actually fix this fluorophore. 38. Improving the View: So with all this talk of views and testing, there is a bug that we haven't addressed yet. It's the same sort of bug that we had here in was published recently. Except in the views where we have our index view where we show all the poles, we are returning a list of questions ordered by pub date. There is no limit on what time those should be ordered on. So if you have a future one that's upcoming, then it will be shown right now. So we don't actually want that. What we want first of all is to use the time zone from Django to fix all of that. So up at the top of the poles slash views dot p, y, we are going to import the time zone. So if worth times m before, and then we are going to change this query. Like so. So we are returning the question objects and we're going to use the filter method. It's the database comes with the filter method where we filter everything with the pub date equal to time zone now and order by minus pub date, right? So what this is basically doing is saying get everything from now and in the past, and then order it by a descending date, time. Now obviously we can't see this in our particular app. Well, because it's not running for a start, let's exit from here and let us run our server. So what's up, et cetera, et cetera. If we added a poll that was in the future, then we would not, in theory be able to see it. So if I add a question in my admin site, question, is this the future? And then the datetime, we can click this handy little Django icon that appears for us, let's say December 31st. And just put the time is now. So we can save that. And then we can go back to our poles. And we don't have access to that. But if I go back to admin, like goatee, is this the future? And I set the publish as today and save it, then we will be able to see it if we refresh the page. So is the future is there. So I've just gone forward. I'm going to change that as sort of my test case. That if I can see that then I know that something is wrong. So we've just changed our index view to fix that particular problem. If we were using, I don't know why this keeps going down. If we were using the old index view, we'd be able to see all of those questions. So we fixed that same sort of class of bug in the index view. 39. Adding Tests for the Polls View: Previously we tested the view. If I just scroll up here inside of our Django environment where we set up a test environment and use the client to get and receive items. Well, now's the time to actually add some tests for the view inside the poles slash test.py. So the very first thing we need at the top is the reverse class that allows us to go from view to URL. So if you recall, we use that actually in a couple of lectures ago when we were testing it in the command line. So we're going to create the following items, functions in our test class. So those are Question model tests. So let's put this in its own class, but in the same file. So we can have a class up here that is the question index view tests. So these are the model test that is different. We're testing the model. Here, we're testing the index view. We're going to have a couple of test cases. So what I'd like to do with my testing, It's just define what the test is cold and the name of the test will actually tell us what the test does. So test for no questions. What is the index view do? Test for past question. What does the index view do? You can probably guess the next one, which is to test for a future question. Then you start to get onto some of the other cases, tests a future question and a past question. And then here's one most people don't think of it, to be honest, I didn't think of this until I read in the Django dogs tested to past questions. So that might be related to the plurality. Who knows? We'll see as we fill the stubs in, right? So first of all, going to test no questions. And that looks a little something like this. So we get a response. And we reverse the poles index view. And then we're going to check that the status code is 200. We are going to check the response says no poles are available. And we are going to search the query. Response contexts. Latest question list is actually empty. So rows, those are all the items that we can assert in this particular case. Just to make sure that all of the things we expect to happen actually happen. For example, if there were no poles in that list, then this Or the page wasn't found rather, then this would be a 404 and my test would fail here. So you can imagine a developer on your team saying, oh, well, why don't we just put a 404 page if there are no poles while the test would fail and hopefully he wouldn't do that. Or at least you would know where to look when the code came back to you or you checked in your code. Okay. Then we're going to go with the past question. So we're going to create a question now, where is that function? While I'm actually going to make one inside of this particular class. Actually all not in the class but in the file itself because I can use this create question. When we come to question the model tests. Remember I referred to the fact that I would extract this function out into its own function. And that's basically what we've done here. So all we do in a creative question is set the time sometime in the days minus days equal today's Yeah. So if we pass in a negative item here, it will set it to sometime in the past because I positive and negative will send it backwards. A positive and a positive, we'll send it forwards. So that's how we just abstract way that function. So in this case past question, we text, we set the text and we put the days at minus 30. And then the response we four, we get from the poles index reversed. And then finally we assert that the query is equal to response contents latest question list, question, past question. So making sure that the past question is actually equal to that, right? So if that works, then we're all good. Okay? I find testing a bit taxing and a big tricky. And to be honest, I think this is a little bit sort of overdone. I've done thick. I'd go really this far unless I was in a big team, a big organization, in which case you kinda have to save for the future question. It's the same idea, but except with setting at 30 days in the future, we're gonna check our responses. No poles are available and the latest question list should be empty. And then of course we are gonna do the same with the next method. So we're going to create two questions because we're testing two questions at minus 30 and plus 30, we're going to check the response and they should only be a single question in there, right? Because of how we've set things up. And then finally, we're going to check to past questions. So past question one, question two. And then going through all the stuff that we've been through already, we're going to check that the responses of those two items are the following. So you can get really specific. And the beauty of a test is you don't have to test generically, did it return an array of two questions? You can check the specifics of each question to make sure that is actually what you're getting back from your tests. Okay, so now of course we can go to a PowerShell. We can shut down that server. We can jump into our shell and we can run. When did we last run these tests? It was a long time ago, wasn't it? And let's paste in our managed PY test, Paul's identified in the shell. I've made a dreadful mistake. Exits and paste. It's getting late in the evening. So there we go. We have R1, all eight tests. That's important to note, even if the test is in its own class, Django knows to go and find each test in those particular classes in the test.py. And this is kind of the structure that it expects. The class, the functions. The class is really for us as the developer to aggregate or congregate our code into sections, classes, et cetera. All the classic stuff we want to do as developers, right? So that's tested everything, everything has pasts and now we're pretty sure that our models and views are correct, at least when it comes to questions. 40. Testing the Detail View and Your Homework: Okay, so in our index view, recall we had this get query set which fed a list of questions with the pub date, time zone now and order minus pub date. What this keeps jumping around. Okay, we need to do the same for the detail view because our end-user, you always need to think about the end user because they will use your rapid ways, you cannot even fathom, okay? Trust me, I've been there, done that many times. The detail view, they could guess the ID of a poll that's coming up and they could actually access it. So we want to make sure they can't do that. And the way we do that is by creating a query set for the detail view, which does something a little like this. So deaf queries itself. We're going to return the question dot objects filter. And we're going to have published LT equals time zone. Now we're putting our filter on there so they can't access anything they shouldn't be able to access. Okay, so that's simple enough. Now I want you to write some tests for this detail view, and I'm gonna give you the stubs that you need for this test section. So we have a class which is the question detail view tests. And then we have this, your code where it's obvious that's where I want you to put your particular code. Test a future question. Okay? So if we're testing a future question and I'm just going to put some text here. So this is the detail view of a question with a pub date in the future and that should return a 404. So you're going to have to check for the response status code, et cetera. It's fairly similar to what we did previously. So have a good think about it. And for the next test, I'm going to paste in Django is description of this hole here. The detailed view of a question with a pub date in the past, which should display the questions texts. So you're going to create a question in both cases. And then you're going to test the response of that index view using that reverse function. And you should come out with stuff similar to, well, various sections here. There isn't one way to create a test. Of course, there are many ways of creating a test as you've seen. So I'll leave that homework with you and I'll see you in the next lecture. After you've had a good go at it, don't come back until you've tried this. 41. Solution to Writing Test Homework: Kay, let's create a future question, which is equal to create question, question tags equals future question days equals five. And remember this create question we defined way up here outside of both classes so we can actually access it everywhere. Well, in this particular file. Okay, so this feature question is five days in the future. Of course, that could be anything. It could be 1 second in the future because this test will take less than a second to run. If you did it one millisecond in the future, you might not get the response that you thinking you would get that. That's an interesting question. If you're curious, you can do something like that. See, see what kind of response you get. Then of course, we need the URL that we're going to fire this on. So we're gonna reverse the poles detail. And then we're going to have the arguments as the future question ID or dot id, which comes from up here. Okay? And then we're gonna get a response from that. Hopefully, we'll get a response from that, which is the self dot client dot get URL. And once you've got that response, we can then do an assertion that they are equal. So the status code should be 404, and that's basically what was outlined in the text up here. So if that runs fine, we're all good. And then of course we have the past question and this is very, very similar. Past question is create question in the past, we do the URL, the response, it's basically all the same except we want to assert that the response contains the past question tags. And so Django will go off and pause that whole HTML page for details and try and find that question texts, which of course is the H1 tag up here. So let's actually test that. I'm, I'm not having you on. So let's run those tests. Oh, I've got an error for my tabs and indentation. Allow me to fix that. So I'm going to remove that. Looks all right. Remember we've run into this before and double-clicking tells you whether you have a tab or not. So let's try that again. There we go. Ten tests, perfectly fine. Okay. Now, if I go to my detail page and I remove this H one, I could comment it out, but I went and now I run these tests. We have one failure because assertion error confined past question in response because it isn't in the response. Remember, it doesn't send over the model. It's testing as if this is a real user coming to your site, clicking something and expecting that question to come back. Okay, so we're gonna save that. So if you set up your tests, right, you can see if other developers or o yourself, it's usually yourself that does it. Our messing with the templates unnecessarily when they shouldn't be. Okay, so that's just been a very basic overview of testing in Django. I hope you've learned at least something about tests and why. I think the whole world is moving to the testing case, test-driven development. You've probably heard of that. And if you go for a job interview, they will definitely ask you about that. So if you've enjoyed this section on testing, let's go onto the next section. 42. Setting up CSS Style Sheets: We've covered business logic in Python, we've covered views, we've covered templates. What's missing? Well, it is styling, the stylesheet, the cascading style sheets or CSS as it's commonly known. So let's look at this list of poles in our app, which is at the URL slash poles. And let's change the color of that list as a good way to demonstrate how to include static assets. So I'm just going to close some of this stuff over here. Close URLs, close models, right? Where do we store a static files? Well, you could store static files literally anywhere on your server, but you don't necessarily want to do that for a simple app such as this. What we're gonna do is store them in the polls folder. So I'm gonna right-click poles. I am going to click New Folder, and I'm going to call this static. But remember our namespace and creed, where we have to put a, another folder inside static cold poles. So we actually have poles slash static slash poles to make sure that Django doesn't get confused. I really wish they saw this out and was a bit more clever because it's a bit weird having to pose directories. So if you're using VS code, you can just type slash pose when you create a directory. And that will automatically create two directories for you, ecstatic and then oppose inside of that. Now again, to create a style dot CSS in there. And the style we're going to do is the list-style. And we're just gonna change that to a really obvious color of green. You don't have to worry about indentation, obviously with CSS, but as developers a fussy sometimes we just want it to work, right? Okay, so that's the list style done. Now that's not going to display anything because our index.html, it doesn't know about that static file. So we have to do two things here. First, we have to tell it the HTML file, the template, to load the static files. So now Django knows to go off to the static folder and go and look for whatever CSS we're about to define. Okay? And the way we define that is like we define in any web app really almost anyway, we have a link to a style sheet, which is of type css. And then the link to the style sheet H ref is from the static folder in polls style.css. Okay, so that should now display. But if we go to our app and we refresh it, nothing happens because we've added files. So if we restart our server, say kill the old one, run the server again. And now when you refresh, we have gone green. We have a nice green color on our app. So I'll just run through that again so you know what's going on. Django picks up static directories inside of each app. We namespace our style sheet by putting a slash name of the app in its slash poles. In this case, we create a style.css. You might have many style sheets in here depending on which page you're looking at. So you don't have to let the whole thing. Or you may just want a single style sheet depends on your workflow and what you like doing as a developer. And then an index, we load static, and then we point to the style sheet. And of course, the magic of stylesheets and HTML will do everything else for us. 43. Adding Images to Django Assets: Alright, so our app has a basic list. It would be nice if we could change the background of this particular app. What if we changed it to have an image of obviously can change background color pretty easily using that style.css. But an image is a different use case. We're going to store an image as a static assets. So in static slash poles, we're going to right-click poles. We're going to create a new folder. And we are going to call this image is that's the one. Now we need an image to put in there. And a really great place to find royalty free images is pixabay.com. And so because it's Christmas when I'm recording that, we are just going to save this image as something. And then we are going to copy that over. So let's show in folder. Copy that over. I'm going to go to our current app. Let's go to source code, DJ-1, polls, static polls, images. And I'm just going to drop that in there. And then I'm going out, of course is done an HTML document. I mean, why would you give me an image? Let's try free download this way. Let's download the 19-21. I promise I'm not a robot. That works better. So let's show in folder once again, let's cut that and let's drop it into our images. As I say, you can of course do this from inside VS code. Let's just call it center a k. So now we can import that and it should shell here and the center dot JPEG. So when the style.css, we are going to set the body of this particular image. And we're going to set the background. Now the Django example I give on the website has a GIF file or a GIF file depending on how you say that. Obviously we don't have that, so we're just going to have center dot JPG. And VS code should actually pick that up. So we set our background, we have the URL pretty straight forward how to set the images. And now I'm going to refresh our app and we get nothing. Perhaps we have to restart our server because there is another file. No. Now, what have I done wrong here? Let's zoom out. There we go. It's because the image is absolutely enormous, right? And so obviously, this is always a problem on the web because different resolutions, different screen sizes, you might actually miss what you're putting in there. So that's going to form the next part where I'm going to set you a task. 44. Task Resize the Image: So last time I hinted at the fact that our image is kind of the wrong size, wrong aspect, while it's the right aspect ratio, but it's just way too big for our, our website. So I want you to fix that problem, change something, some property about the image without resizing the image, whatever image you've chosen. I want you to use pure CSS to make it fit the screen so the user can see it in the full page layout. Good luck. 45. Solution Resize the Image: So how was that homework? I suspect that you probably got a stuck somewhere. You saw something about the background image and you thought, I can do something with the image tag. Now most people when they're searching out to resize an image, have found basically the, what, what is it called them looking at my notes here, it's something like object fit property. I'm pretty sure that's it. And that comes with images. But because we're in the background, it's, things are slightly different. So what we need is to add the following line, background, size. And it will tell us what each thing, well, should tell us what each thing means. If we hit cover, that is going to expand the image or shrink the image according to the aspect ratio of our screen. So if I go to reset the page size and I refresh it, there we have center. If I select something else, come on, event. We can have contain. So let's refresh now. And there we go. That's sort of fills the screen. Now. I drop this size down. You'll notice that it dynamically scales, right? So if you're running your web browser and whatever device the background image is always gonna scale. Now, background image is a kind of very Web 1. And it's questionable whether you'll actually do this. But the point of the task was to send you down a rabbit hole of CSS because getting this object fit property on images wouldn't work for this use case because we're using an image in a background. So it's not an image tag, it's the background tag. And CSS has recently, within the past couple of years, added the various image properties that we need. And of course you can click the Mozilla reference there if you so wish, and that will tell you everything you need to know. So even though you're working with Django, you're working with templates. Basic HTML, CSS still apply, okay, so whenever you want to change something in Django or you want to lay something out in a certain way. Always go down the HTML and styling root first. Don't actually try and do it as some jangle way. Just go with the templating first. And then if you need to use your Django skills. 46. Customise the Admin Layout for Question Polls: Now if you recall, we have a Django Admin layout, and this is one of the best parts of Django. We get automatic administration for the models and things that we are creating as long as we register them with the admin section. Now, what happens if I click a question like WhatsApp? We have a question text at the top and we have the pub date and time below. What if I want to customize this particular section? Well, that's really simple. In the poll slash admin dot p y, recall that we imported the model and then we wrote that line of code to register the question, which is what's giving us access to those particular questions to edit. What we're going to add is the following Before we register it. So we're going to have a question Admin, which has a, an object passed over, which is the model admin that tells Django, This is how we're going to do. How are we going to modify the display of these models? And I'm going to tell it we want the fields to be in this particular order, pub date, then question text. So as it currently stands, we have question text, then pub date, right? And then when we registered the question, we also pass in that question, admin. So Django knows what's up. Now if I refresh this page, the two items have switched. So we have date published at the top and question text below. Now we can go one step further than that and we can split it up into field sets. So I'm going to copy that over. So rather than having fields whereabouts, delete this line, but before I do, I'll paste this line to show the difference. A set of fields, groups, logical fields. Ok. So we have this particular item that has no title, but has fields of question text and this one that has a title of day information with the fields of pub date. So if you have a very complex model, then this is how you go about organizing it in your admin. So if I refresh this page, note how it puts a section in saying date information. So now you can organize things in a neat way and separate things out so that people can see what's going on. Okay, that's it for modifying how we display the fields and the field sets. There are many, many more options for modifying your models in admin. And if that's something that you're gonna do, usually it's an enterprise type application where people are entering data, where you might have to do stuff like this, then it's worth actually going and reading UP more about this. 47. Task Adding Question Choices in Admin: So far we have questioned admin so we can edit questions. But what if I want to add choices? Well, obviously I want to add choices for each question because I'm not going to rely on whatever I put into the command line. So I want you to perform a task for me, which is to add a choice admin in the same manner we have added a question admin in here and register that model with Django so that we can enter, delete, remove, assign choices, etc. Now, this isn't totally straightforward if you think too much about it. But all I want you to do for homework is just register the choice and then play around with it. When it comes to linking the choice to questions, we will cover that in the future. 48. Solution Adding Question Choices: Right, so that's pretty straightforward. What we're gonna do is import the choice model. And then we're going to add one more line down here. The admin dot site, DOT register. The choice. Now you can't put choice up unless line, it has to have its own line where it registers itself as far as I'm aware, or maybe wrong about them. Now if you created a choice admin to mess around with fields and fields sets, that's great, good work. The best way to learn is by exploring, but I'm just going to leave it as this very simple version for now. We're gonna go back here, I'm going to refresh the page. And there we have a list of choices. So I can click that. And then we have the three choices that we had before. And the choices belonged to a question. We have H0 is text and we have the number of votes so we can edit everything. Now notice that Django automatically picks up the primary key of the question, right? So it knows that it choice has a question referred to in another table of the database. So play it, displays it here. And that's also why we have the double underscore def definition now STR function in our models. So we don't just get some weird texture or a number or an ID, we actually get the text of the question or something that makes sense to the human editor. Because you can imagine you make a commercial app stuff. I'm not going to know that ID 1-2-3 69 refers to X, Y Zed question, they need something that's human readable. So that's really why we do it. One of the few places you see it in action. And we have choice, text and votes. So that was simple enough, but we can move on to make this just a little more complex and more usable. 49. A better way to Add Choices to Admin: Our application allows us to edit questions and choices, but it doesn't really make sense from our standpoint, yes, some questions might have the same choices associated with them, but in a poll that's probably very, very rare, exceedingly rare. So be much better for good editor question directly and edit the choices inside of that question directly. So what we're gonna do is undo all of our hard work by removing this registered choice section. We're going to keep the import for choices in the admin dot P Y. But up here we are going to register a new class, which looks a little something like this, where we have a choice in line. So we have a special item here which is stacked in line and you'll see what that means in a minute. We pass over the model, which is a choice, and we give it three extra spaces and you'll see what that means as well. So basically we just configuring how our choices are going to display in this particular question. And then we have to add one more thing, which is an inlines property outside rather of the field sets here. Okay? So we're saying the inline properties attached to this question come from choice in line which we've defined here. So let's save that and let's refresh the questions page. So now the choices is gone as you might expect. But if I click what's up, we have the question text date published, and now below, we have choices in line. Okay. And that is what it's referring to when it says stacked in line, we're literally stacking them on top of each other. And then we have each property for the choice, not much votes for three, et cetera. Down below that, we have three extra spaces. Now you don't have to have that. You can click Add another choice. And to be honest, it does look a lot better in my opinion. If you just give the user an option to add another day or genetic whatever it is, and then we can save that. So I think this looks a lot better than adding extra spaces, but it depends how you want to code this. So that's how we edit the choices in line. And now when I save these choices, they get automatically assigned that foreign key, the foreign ID of the question. It's brilliant, Django, when, when you apply it to the right kind of project that you need to do this sort of thing in. Okay, so what else have we got here? This takes up quite a lot of space, right? It's a simple choice. It shouldn't take up 34 lines worth of space. So what we're gonna do is go back to our code and we are going to change this to tabular in line. And in fact, if I just type in line, those are the two options. Go back here and refresh the page. And there we go. We have a tabular inline, so puts it next to each other. And that is obviously much easier to read and easier to edit. 50. Customise the Admin Change List and a Task: The next thing that we're gonna do is jazz up this list of questions. What if I want to see when questions are published or when they're going to be published in case of our future questions where we need to add the column for that on this particular page. So how do we tell Django about that? While we go to the admin dot py file and in the question admin object or class, we are going to add the, a special parameter called list display. And this tells Django, I want you to show the following properties, question texts, and in this case the pub date. So if we go back to our project and we refresh the page, notice that the column now appears over on the right-hand side with date published. And of course, you can add any of the question of properties in here. Now it's your turn. I want you to add the following item. So this isn't code by the way, this is just me saying what I want you to add. I want you to add that to one of the columns. So feel free to pause the video now and I'm going to give you the solution in a second. Right? Did you try it? Well, that should be fairly simple because here, which is added to the list. And of course, when we go back and refresh, this tells us neither of those was published recently. Recall the recent was within one day of today's date. In the past. 51. Allow Sorting on Custom Fields Like Recently Published: K. What I didn't mention last time was that we can sort items by clicking the header text inside of the admin section. So can I click was published recently, Noel? No, I can't. Can I click date published? Yes, I can. And that's because Django understands texts alphabetical. It understands dates, time, but was published recently, is kind of a weird case where it doesn't understand how you want to sort these things. So what we're gonna do is actually add a filter here so that we can also sort on that field. So what we're gonna do to do that is go to poles slash models dot p y. And in the question section underneath was published recently but indented like you see here. We're going to add three lines. So we are going to tell it the admin order field is PUB date. So what order do you put things in? You can put it in by the published date. And then we're going to tell it was published recently. Boolean is true and was published recently. Short description published recently, will see what all these mean and what they do in a minute when we load up the interface. But finally, there's something we have to do in admin dot p y, which is to drop in this property of a list filter. So we're telling it the list filter is PUB date. So now if I refresh this page, we have a published recently column, right? And when I click it, I can actually sort on it, right, so I can get the most recent ones up to the top or vice versa because we click it twice, it does it in the reverse order. And you'll notice that edge has picked up true or false as this icon here. And if we right-click and inspect, we can see I conditional dot SVG. This comes from Django apparently, but it didn't actually appear before. We had a basic true or false, which is a little bit weird. So what we're doing here is adding this filter by pub date. And we are allowing Django to sort that item by giving it these extra properties. Now, would you really do this in reality? Well, I've done it once or twice, but I think if you architect the upright, you don't really need to do this. Okay, that's it for sort of customizing the model interface in Django. We're going to actually move on to the next section now where we customize the actual HTML. Sitting behind the admin. 52. Customising Project Templates and a HARD Task: Let's move on to the last part of the official Django tutorial, which is how to customize the look of the admin interface as in the style is what I mean in this particular case. So the first thing we're going to look at is finally we're moving outside of the poles folder and we're getting into DJ-1. We are going into settings. Dj-1 is just the name of the app. Yours may be different to mine. And we're going to scroll down in this settings area until we see the templates, object or array. Okay, now this just defines some stuff and what we're going to tell it is that we want you to use a custom template. So I'm going to paste the following item here, where it takes the base directory of the application and looks for the templates folder. So this is one of the things that Django doesn't do automatically. You have to specifically explicitly rather tell it that I want you to look in the templates folder. Now there are many, many template directories. We have template directory in our polls, but this is for the admin. Now don't be tempted to just lump ALU templates in one directory. And especially in the main application directory, we're going to create a templates folder because that's what we've told Django to expect. Don't put any of your individual app templates in here. It's pretty tempting just to create a post directory in here. So all your templates that are in one folder, but don't do that. Now again, the name spacing applies here. So inside of templates, not new file, we have to create a new folder called admin. So now that we know our templates for admin will be found in that particular folder. We can go ahead and create a template, but that's pretty complex as templates go. And of course, it is responsive. And the more I move it around, the more responsive it changes for us. So what we have to do is find where that original template is and copy it over to this Templates folder. So that's the basis of how we modify templates. In the admin section, we find the original and we modify it. Now to find the original, There is a special command that you can run on Windows, and I believe it's pretty much the same command for Mac and Linux. And the command is the following. I'm not, not, not t. As a short command is this PY dash c. And then in quotes import Django, print Django path. So this is going to tell us where Django is stored on our system. And in my particular case, it is stored here under lib site packages slash Django. So that should be the case for you as well. It should be in the folder where your environment is, right. If, if that comes out, sorry, wherever we, if that comes out as something like the Python folder in your C drive, then you haven't set up your virtual environment correctly. So have a look at that. Before you get your apps in a big mess. So I can go to, up here, we can go to source code, ENV, lib site packages. And it is just plain Django. There it is. Okay, and now we have a template folder here. We can double-click that. Is that the actual template folder? Let me just check. This is the kind of thing you only do. A couple of times. You always forget where it is. Now it's in Django, contrib, add min, templates, admin. And then the one that we're going to modify is, I'm really lucky I wrote notes here is base site. So I'm gonna copy that base site. Then I'm gonna go back to my source code, go into the app, go into the main application templates, admin. And I am going to paste that Straits into here. Okay, so we've set up the template and now we have access to the base site HTML. And if you open it, it, it tells you that it extends the base dot HTML. So now actually editing the entire template because we are inheriting templates and properties from other HTML files. That's another feature of Django. We have a block title, subtitle, block branding, et cetera. So what should we change in here? Well, let's change the block branding because we don't just want admin to display. And I've got something that I've prepared earlier where it's basically the same H1, but we have a link to the admin index and we have poles administration. So I'm going to save that. Now. Once we've saved it, you can restart the server. I've already reached out to my server and check this and have checked that it doesn't work. And I thought it's worth pointing out to you. Sometimes these tutorials have really small things that mess you up. So this Templates admin folder doesn't actually sit in the app folder itself. This Templates admin folder is going to be cut and sit in the parent directory. So we might actually have to reveal in the File Explorer, go up one, we're going to cut that templates. And then we're gonna go up one more. See your directory structure is like this and we're going to paste it into there. Okay, so now I'm just gonna restart. And now when I refresh, we have what we're expecting which has poles administration. So just to make that clear, don't put it in the top-level application, but put it in the level above that. So it sits outside of everything. That wasn't very clear on Django website when they did the tutorial. And in fact, there's a StackOverflow page with lots of people say it doesn't work, I don't know why. And that was the solution. So anyway, that's, that's a good introduction to bug fixing. So in templates slash admin, you can put all of the templates. Now, I'm going to set you a very hot task. What I want you to do, I'm just gonna read off my notes here is to customize again the admin index page. Because there is an index.html somewhere in the templates folder in the base Django installation. I wanted to find it and customize it so that the end user cannot click the Change item. So what do I mean? So that they cannot click this change questions. Okay, we want to totally remove that element. So that will give you a good start into actually customizing your templates. Good luck. 53. Solution Customise Admin Index Page: So how did you find that? I hope that was fairly straightforward. If you recall, we had this special command here where we would import Django, print the Django path, which gives us the Django pathway to look. You should know that that is now inside of your environment. To environment lib site packages, Django, contrib, admin, templates, admin, and it's the index.html that we're after. So we are going to right click and copy that. We're going to put that in the correct folder this time. And I'm talking to myself there of the DJ-1 templates admin. And we're just going to drop that straight into their k. So now we have access to it over here. And what did I ask you to do? I wanted you to remove this change button. So coming back to our code that we just have to read through the whole thing and see what's going on. It's actually a good way to learn. Django is reading through the basic templates because that tells you how things should be architected. A k. So we have this block here, which has an app list. That's interesting. So the app list is this under poles. And then with app list, app list, show change links equals true. Well what happens if I drop a false into that? And now I've saved it. If I reload, everything breaks because I don't do the classic develop a problem. I have the classic developer problem, never reloading. Now if I reload the page, change has gone. That doesn't necessarily mean they can't change it. So what do I mean? Well, let's do the following. In our index.html, which is scroll down. Let's change this back to true. Doing some hacking here, see what happens. I've saved it. We can change it. If I click change. Now we have this URL. So I'm going to copy this URL. And then I'm going to change this to false. And we're gonna go straight to that URL again. Changes disappeared as a button, but I can still go straight to the URL. So you have to be aware if this applies to any language, any platform. If you change just the front end, obviously, the backend needs to be changed as well. So if you didn't want to use it to change things with Django, you'd actually set up a user roles so they couldn't do that. But if you didn't want that, then you have to change the back end in some way. Ok, that is pretty much it for customizing the admin interface. Obviously there's a lot more that we could dive into. But I just wanted to be true to the original Django Getting Started tutorial. And so now you should have enough so that when you want to make something custom, not just in the site admin, but anywhere in your Django project that you know where to start looking and you know what to start modifying. So whatever your task is with Django, whatever you're going to make, good luck with that. And in the next section, I'm going to set you just a little challenge, a little homework, which is basically to repeat this course from memory and create a slightly different applications. So stay tuned for that one. 54. Thank You and Your Class Project: Well done for completing this course. Many people don't actually get to this point. You'd be surprised at how few complete things that they've paid good money for. So as a reward, I'm going to give you a little task. I want you to repeat everything you've seen without watching the videos. If you have to watch a video here and there to remind yourself, that's fine as well. What I want you to do is to recreate this Polls app, totally fresh code base so that you ask someone what their favorite color is or what color are their eyes and all, what, what age are they, etc, etc. Loads of different polls that you can do, but basically recreate what you've seen here where you ask people questions. So it, it does seem a bit annoying that you actually have to redo everything you just did. You think that's not really conducive to learning, but that's not true. Doing things twice helps you learn ten times as efficiently. That's what I've found. That's what our founders, all of my students and the people that do that are the ones that really get ahead in life. So anyway, that's enough of me. Rambling. Software developers don't get out much and don't see many people. So when they get on video, they tend to ramble a lot. Anyway. Well done. And I'll see you next time.