1 00:00:00,000 --> 00:00:17,790 2 00:00:17,790 --> 00:00:20,040 SPEAKER: OK welcome back, everyone, to Web Programming 3 00:00:20,040 --> 00:00:21,375 with Python and JavaScript. 4 00:00:21,375 --> 00:00:23,250 So thus far in the course, we've taken a look 5 00:00:23,250 --> 00:00:25,080 at a couple of different technologies. 6 00:00:25,080 --> 00:00:27,810 We took a look at HTML and CSS which are languages 7 00:00:27,810 --> 00:00:30,300 we can use to describe the structure of a web page 8 00:00:30,300 --> 00:00:32,037 and then style it appropriately. 9 00:00:32,037 --> 00:00:34,620 And then we also took a look at Python, a programming language 10 00:00:34,620 --> 00:00:36,912 that we could use to get programming features, features 11 00:00:36,912 --> 00:00:40,260 like loops, and conditions, and variables, and functions and more. 12 00:00:40,260 --> 00:00:42,900 And what we're going to do today is introduce Django, 13 00:00:42,900 --> 00:00:44,400 which is going to combine these two. 14 00:00:44,400 --> 00:00:47,010 Django is a Python web framework which is 15 00:00:47,010 --> 00:00:51,270 going to allow us to write Python code that is able to dynamically generate 16 00:00:51,270 --> 00:00:56,770 HTML and CSS, ultimately allowing us to build a dynamic web application. 17 00:00:56,770 --> 00:00:58,770 So what is this now going to enable us to do 18 00:00:58,770 --> 00:01:02,820 that we weren't able to previously do using just HTML and CSS? 19 00:01:02,820 --> 00:01:08,070 Well, using HTML and CSS, the web pages that we created were static web pages. 20 00:01:08,070 --> 00:01:08,970 They were the same. 21 00:01:08,970 --> 00:01:12,000 And every time you visited the web page, the page you were looking at 22 00:01:12,000 --> 00:01:13,028 was identical. 23 00:01:13,028 --> 00:01:14,820 But ultimately, if you think about websites 24 00:01:14,820 --> 00:01:17,730 you interact with on a day to day basis, say like the New York Times, 25 00:01:17,730 --> 00:01:19,980 for example, it's not the case that every time you 26 00:01:19,980 --> 00:01:23,490 go to the New York Times home page you're seeing the exact same HTML. 27 00:01:23,490 --> 00:01:24,120 It changes. 28 00:01:24,120 --> 00:01:26,130 The next day, you might see the new date. 29 00:01:26,130 --> 00:01:27,840 You might see the next day's articles. 30 00:01:27,840 --> 00:01:30,673 Whenever someone comments in an article, you can see those comments. 31 00:01:30,673 --> 00:01:34,050 And you can see how many people have commented on an article, for example. 32 00:01:34,050 --> 00:01:36,090 And it's probably not the case that someone 33 00:01:36,090 --> 00:01:39,210 is sitting there updating the HTML of the New York Times website 34 00:01:39,210 --> 00:01:41,888 every time something changes, but rather more likely 35 00:01:41,888 --> 00:01:44,430 there is some program, maybe written in Python, maybe written 36 00:01:44,430 --> 00:01:46,590 in some other language, that is dynamically 37 00:01:46,590 --> 00:01:49,530 generating that HTML and CSS and allowing the web 38 00:01:49,530 --> 00:01:53,310 application to respond based on how users are interacting with it. 39 00:01:53,310 --> 00:01:55,980 And this is ultimately what we're going to be trying to create 40 00:01:55,980 --> 00:01:57,582 using this Django web framework. 41 00:01:57,582 --> 00:02:00,540 We're going to be creating software that's going to run on a web server 42 00:02:00,540 --> 00:02:02,730 such that clients, running in their web browser, 43 00:02:02,730 --> 00:02:04,750 can make requests to our web server. 44 00:02:04,750 --> 00:02:08,080 And our server is going to respond back with some sort of response. 45 00:02:08,080 --> 00:02:10,090 And so how does this process actually happen? 46 00:02:10,090 --> 00:02:12,190 Well ultimately, it boils down to this protocol, 47 00:02:12,190 --> 00:02:15,750 HTTP, otherwise known as the HyperText Transfer Protocol, 48 00:02:15,750 --> 00:02:18,000 which is the protocol for how messages are going to be 49 00:02:18,000 --> 00:02:20,340 sent back and forth over the internet. 50 00:02:20,340 --> 00:02:23,670 In this case, you can think about this as a computer, some user, which 51 00:02:23,670 --> 00:02:26,590 we might call the client, and then our server, 52 00:02:26,590 --> 00:02:30,000 which is going to be the server that is going to contain our web application. 53 00:02:30,000 --> 00:02:32,370 We are going to write a Django web application that 54 00:02:32,370 --> 00:02:33,900 is going to run on this server. 55 00:02:33,900 --> 00:02:36,180 And clients are going to make requests to that server. 56 00:02:36,180 --> 00:02:38,250 The server is going to process that request 57 00:02:38,250 --> 00:02:41,100 and then return some sort of response. 58 00:02:41,100 --> 00:02:43,410 That request might look a little something like this. 59 00:02:43,410 --> 00:02:45,743 Underneath the hood, it might start with the word "get." 60 00:02:45,743 --> 00:02:48,150 Get is just an example of a request method, 61 00:02:48,150 --> 00:02:49,830 a way that you might try to get a page. 62 00:02:49,830 --> 00:02:53,580 So get just means, I would like to get a particular page. 63 00:02:53,580 --> 00:02:56,130 In this case, the page I'm trying to get is the slash page, 64 00:02:56,130 --> 00:02:58,620 just denoting the root of the website, usually 65 00:02:58,620 --> 00:03:00,090 the default page for the website. 66 00:03:00,090 --> 00:03:03,930 HTTP 1-1 is just the version of HTTP that we're using. 67 00:03:03,930 --> 00:03:07,320 And the host is what URL we're trying to access the web page for, 68 00:03:07,320 --> 00:03:10,770 so example.com or some other website, for example, that I 69 00:03:10,770 --> 00:03:12,840 might be trying to make a request to. 70 00:03:12,840 --> 00:03:16,290 So this request ultimately gets sent by my web browser, when I type 71 00:03:16,290 --> 00:03:18,450 in a URL and press Return, for example. 72 00:03:18,450 --> 00:03:22,963 The server processes that request and then gives back some sort of response. 73 00:03:22,963 --> 00:03:26,130 And the response will generally look a little something like this, beginning 74 00:03:26,130 --> 00:03:30,300 with the HTTP version 1.1, or in some cases 2, version 2 now, 75 00:03:30,300 --> 00:03:33,110 and then some sort of response code. 76 00:03:33,110 --> 00:03:35,940 200 is a response code the just means OK. 77 00:03:35,940 --> 00:03:37,080 Everything was OK. 78 00:03:37,080 --> 00:03:39,880 The page was returned successfully, for example. 79 00:03:39,880 --> 00:03:43,440 And here on the second line, we see Content-Type text/html, 80 00:03:43,440 --> 00:03:46,950 which just means the format of the data that's coming back in this response 81 00:03:46,950 --> 00:03:47,945 is HTML data. 82 00:03:47,945 --> 00:03:51,540 It's HTML data that the user's web browser on the client 83 00:03:51,540 --> 00:03:53,702 should then render as HTML, for example. 84 00:03:53,702 --> 00:03:55,410 And there are other content types as well 85 00:03:55,410 --> 00:03:58,050 and other information that comes back in that response, 86 00:03:58,050 --> 00:04:00,390 but the key idea is just thinking about the web 87 00:04:00,390 --> 00:04:02,640 in terms of requests and responses. 88 00:04:02,640 --> 00:04:05,700 One user makes a request to get a particular web page. 89 00:04:05,700 --> 00:04:08,910 And what they get back is a response like this that hopefully includes 90 00:04:08,910 --> 00:04:11,310 status code 200, which means OK, but there are 91 00:04:11,310 --> 00:04:13,140 a number of other status codes as well. 92 00:04:13,140 --> 00:04:14,330 Some of the more popular-- 93 00:04:14,330 --> 00:04:15,270 200 means OK. 94 00:04:15,270 --> 00:04:18,010 You've probably seen 404, the status code 95 00:04:18,010 --> 00:04:21,269 that means not found, if you try to request a web page that doesn't exist, 96 00:04:21,269 --> 00:04:22,380 for example. 97 00:04:22,380 --> 00:04:25,320 Others you might see or 301, moved permanently. 98 00:04:25,320 --> 00:04:27,900 That often happens if you're redirected from one website 99 00:04:27,900 --> 00:04:29,250 to another, for example. 100 00:04:29,250 --> 00:04:31,620 We'll take a look at an example of that shortly. 101 00:04:31,620 --> 00:04:33,775 403 generally means forbidden. 102 00:04:33,775 --> 00:04:36,650 You're trying to access a page you're not supposed to have access to. 103 00:04:36,650 --> 00:04:40,170 And 500 usually refers to some sort of internal server error, 104 00:04:40,170 --> 00:04:42,380 usually meaning that whoever was writing the server, 105 00:04:42,380 --> 00:04:45,818 maybe our Django web application, has something that's buggy in it. 106 00:04:45,818 --> 00:04:47,610 And we might need to go back and figure out 107 00:04:47,610 --> 00:04:49,650 where that bug is in order to fix it. 108 00:04:49,650 --> 00:04:51,690 And again, there are other status codes as well, 109 00:04:51,690 --> 00:04:53,160 but these are some of the most common that we're 110 00:04:53,160 --> 00:04:56,220 going to be seeing and interacting with as we go about building 111 00:04:56,220 --> 00:04:59,010 web applications using Django. 112 00:04:59,010 --> 00:05:00,550 So Django is a web framework. 113 00:05:00,550 --> 00:05:02,340 It's a set of tools that is already built 114 00:05:02,340 --> 00:05:05,340 for us that's just going to make it easy to get started by writing 115 00:05:05,340 --> 00:05:09,930 Python code in order to design a fully fledged web application by taking care 116 00:05:09,930 --> 00:05:11,880 of some of the stuff that most web pages all 117 00:05:11,880 --> 00:05:15,690 have to do such that we can focus on the interesting logic of building 118 00:05:15,690 --> 00:05:17,530 our own web applications. 119 00:05:17,530 --> 00:05:21,310 So the first step you'll need to do in order to use Django is to install it. 120 00:05:21,310 --> 00:05:24,430 If you're using Python, you may be familiar with pip, the Python package 121 00:05:24,430 --> 00:05:28,030 manager, which makes it easy to be able to install new packages. 122 00:05:28,030 --> 00:05:30,830 And the first thing we'll probably want to do is install Django. 123 00:05:30,830 --> 00:05:32,580 And here we're going to be using Python 3, 124 00:05:32,580 --> 00:05:35,650 so you might need to specify pip3 install Django 125 00:05:35,650 --> 00:05:38,420 to install Django on your system. 126 00:05:38,420 --> 00:05:42,700 And after you do, we can run a command to be able to create a Django project. 127 00:05:42,700 --> 00:05:44,890 And that command looks something like this, 128 00:05:44,890 --> 00:05:49,480 django-admin startproject followed by the name of the project 129 00:05:49,480 --> 00:05:50,740 that we would like to create. 130 00:05:50,740 --> 00:05:53,140 When we run this command, Django is automatically 131 00:05:53,140 --> 00:05:55,420 going to create some starter files for us 132 00:05:55,420 --> 00:05:59,530 just to get started with the process of building a web application. 133 00:05:59,530 --> 00:06:02,920 So let's try creating a project now I'll go ahead and go into my terminal 134 00:06:02,920 --> 00:06:03,933 window. 135 00:06:03,933 --> 00:06:05,850 And what I'm going to do is type that command. 136 00:06:05,850 --> 00:06:10,000 I going to type django-admin and then startproject. 137 00:06:10,000 --> 00:06:12,050 And then I need to give this project a name. 138 00:06:12,050 --> 00:06:15,460 And I'm just going to give this project a name of lecture3, for example. 139 00:06:15,460 --> 00:06:17,680 You can name the project whatever you'd like. 140 00:06:17,680 --> 00:06:20,080 And when that happens, when I type ls, what I see 141 00:06:20,080 --> 00:06:25,270 is that a lecture3 directory or folder has been created for me by Django. 142 00:06:25,270 --> 00:06:28,390 And if I go ahead and open this up inside of my text editor, 143 00:06:28,390 --> 00:06:34,010 we can take a look at what files are inside of this lecture3 directory. 144 00:06:34,010 --> 00:06:38,270 So actually, I'll go ahead and cd into lecture3 and open it up. 145 00:06:38,270 --> 00:06:41,080 And what we see inside of lecture3 are a couple of files 146 00:06:41,080 --> 00:06:42,638 that we're given just to get started. 147 00:06:42,638 --> 00:06:43,930 So there are a couple of files. 148 00:06:43,930 --> 00:06:46,388 And not all of them are really interesting to us right now. 149 00:06:46,388 --> 00:06:49,097 But first and foremost, manage.py is a file 150 00:06:49,097 --> 00:06:50,680 that Django is going to create for us. 151 00:06:50,680 --> 00:06:52,870 We'll generally not need to touch that file, 152 00:06:52,870 --> 00:06:57,100 but we're going to use the manage.py file to be able to execute commands 153 00:06:57,100 --> 00:06:58,360 on this Django project. 154 00:06:58,360 --> 00:07:00,310 And we'll see a couple of examples of that now 155 00:07:00,310 --> 00:07:02,810 and a couple of examples of that in future lectures as well. 156 00:07:02,810 --> 00:07:05,185 And then the other important one that we'll be looking at 157 00:07:05,185 --> 00:07:08,260 is settings.py, which just contains important configuration 158 00:07:08,260 --> 00:07:10,450 settings for our Django application. 159 00:07:10,450 --> 00:07:13,190 Settings.py comes preloaded with a couple of default settings, 160 00:07:13,190 --> 00:07:15,190 but we might want to change those settings to be 161 00:07:15,190 --> 00:07:17,800 able to add features to our application as well 162 00:07:17,800 --> 00:07:20,950 or make some modifications to how the application behaves. 163 00:07:20,950 --> 00:07:25,750 And then the other important file that's pre-created for us now here is urls.py. 164 00:07:25,750 --> 00:07:29,620 And you can think of urls.py as a sort of table of contents for our web 165 00:07:29,620 --> 00:07:32,620 application, that on any given web application, on a website, 166 00:07:32,620 --> 00:07:35,800 there are a number of different URLs or routes that you can visit. 167 00:07:35,800 --> 00:07:39,250 On Google, for example, you can visit /search or /images. 168 00:07:39,250 --> 00:07:42,520 And urls.py is just going to be a table of contents 169 00:07:42,520 --> 00:07:47,340 of all of URLs on my web application that I can ultimately visit. 170 00:07:47,340 --> 00:07:49,810 And so if I'd like to try running this web application just 171 00:07:49,810 --> 00:07:53,950 to see what it looks like, the way to do it in Django is, in the terminal, 172 00:07:53,950 --> 00:07:58,150 I'll run python manage.py followed by a command. 173 00:07:58,150 --> 00:08:00,050 And the command is called runserver. 174 00:08:00,050 --> 00:08:02,230 And so this is generally how we'll use manage.py. 175 00:08:02,230 --> 00:08:05,680 We'll manage.py followed by an argument specifying 176 00:08:05,680 --> 00:08:07,210 what command we would like to run. 177 00:08:07,210 --> 00:08:12,610 And python manage.py runserver means go ahead and actually run this web server. 178 00:08:12,610 --> 00:08:15,527 And if I do this, I'm now running this web server locally. 179 00:08:15,527 --> 00:08:17,110 And I see a bunch of debugging output. 180 00:08:17,110 --> 00:08:19,900 But the interesting point to me is that it says down here, 181 00:08:19,900 --> 00:08:26,200 starting development server at http://127.0.0.1:8000 182 00:08:26,200 --> 00:08:28,390 And so this is where my web application is currently 183 00:08:28,390 --> 00:08:33,010 running, 127.0.0.1 is an IP address, an address on the internet that 184 00:08:33,010 --> 00:08:36,539 just refers to my local computer, the computer I'm looking at right now. 185 00:08:36,539 --> 00:08:39,340 So only I can access this, not anyone else on the internet. 186 00:08:39,340 --> 00:08:42,250 And 8000 is what we would call a port number. 187 00:08:42,250 --> 00:08:45,163 It just refers to what type of service is being run. 188 00:08:45,163 --> 00:08:47,080 And you might have multiple different internet 189 00:08:47,080 --> 00:08:48,850 services running on different ports. 190 00:08:48,850 --> 00:08:53,050 In this case, our Django application is running on port 8000. 191 00:08:53,050 --> 00:08:56,590 So if I copy this URL and just go ahead and go into my web browser 192 00:08:56,590 --> 00:09:01,030 and paste that URL in, what I see is just Django's default page to say, 193 00:09:01,030 --> 00:09:02,770 our installation of Django works. 194 00:09:02,770 --> 00:09:04,870 And this now is the default page the Django 195 00:09:04,870 --> 00:09:07,120 is going to give to us to say that Django has 196 00:09:07,120 --> 00:09:08,860 been loaded for this web application. 197 00:09:08,860 --> 00:09:12,280 And we can now actually start to begin building this web application 198 00:09:12,280 --> 00:09:14,950 by adding the features we want to it. 199 00:09:14,950 --> 00:09:18,430 And so what we've created here is what we call a Django project. 200 00:09:18,430 --> 00:09:20,890 And in terms of the way a Django project is structured, 201 00:09:20,890 --> 00:09:25,240 is that every Django project generally consists of one or more Django 202 00:09:25,240 --> 00:09:26,380 applications. 203 00:09:26,380 --> 00:09:30,080 So one project may have multiple applications within it. 204 00:09:30,080 --> 00:09:33,250 And the reason for this division is, if you think about big websites, 205 00:09:33,250 --> 00:09:35,740 oftentimes a big website, a big project has 206 00:09:35,740 --> 00:09:38,830 multiple different apps that are sort of separate services 207 00:09:38,830 --> 00:09:40,090 that operate within it. 208 00:09:40,090 --> 00:09:43,360 Google, for example, has Google search, but also Google Images, and Google 209 00:09:43,360 --> 00:09:45,130 News, and Google Maps, where you can think 210 00:09:45,130 --> 00:09:48,220 of each of those individual services as a separate app 211 00:09:48,220 --> 00:09:51,610 all under one larger project like Google, for example. 212 00:09:51,610 --> 00:09:54,617 Or Amazon's website, for instance, might be one big project 213 00:09:54,617 --> 00:09:57,700 that has a couple of different apps within it, one app for shopping maybe, 214 00:09:57,700 --> 00:10:00,860 and one application for Amazon's video service, for example. 215 00:10:00,860 --> 00:10:04,030 And so Django comes preloaded with its ability to take a project 216 00:10:04,030 --> 00:10:06,290 and divide it into multiple distinct apps. 217 00:10:06,290 --> 00:10:08,290 Maybe for simpler apps, we're only going to have 218 00:10:08,290 --> 00:10:11,320 a project that has one app inside of it instead of multiple. 219 00:10:11,320 --> 00:10:14,440 But it has the ability to allow us to create these separate apps that 220 00:10:14,440 --> 00:10:16,710 have different capacities. 221 00:10:16,710 --> 00:10:18,570 So if we want to get started with Django, 222 00:10:18,570 --> 00:10:22,260 the first thing we'll need to do after we create a project is create a Django. 223 00:10:22,260 --> 00:10:25,800 App and so the way to do that, so go back to the terminal. 224 00:10:25,800 --> 00:10:27,600 I'll go ahead and exit out of this server 225 00:10:27,600 --> 00:10:31,672 by pressing Control C to say I would like to exit, stop running the server. 226 00:10:31,672 --> 00:10:33,630 So now if I go back to that URL and refresh it, 227 00:10:33,630 --> 00:10:37,140 it won't work, because the Django server is no longer running. 228 00:10:37,140 --> 00:10:42,300 But I'll run the command python manage.py startapp and then 229 00:10:42,300 --> 00:10:44,300 the name of the app that I would like to create. 230 00:10:44,300 --> 00:10:48,730 And for our very first app, I'll just call this app hello, for example. 231 00:10:48,730 --> 00:10:51,880 So I've now created one Django project called lecture3, 232 00:10:51,880 --> 00:10:54,720 and inside of which I've created the very first app that I've now 233 00:10:54,720 --> 00:10:56,490 called hello. 234 00:10:56,490 --> 00:10:58,980 And so if we take a look at our files now, 235 00:10:58,980 --> 00:11:01,500 you'll see that, in addition to a lecture3 directory which 236 00:11:01,500 --> 00:11:06,000 existed before, we also now have a hello directory the Django created for us 237 00:11:06,000 --> 00:11:09,863 that is going to contain the information needed for the hello app to work. 238 00:11:09,863 --> 00:11:12,780 And there are a number of files, again, that are generated by default. 239 00:11:12,780 --> 00:11:14,655 We're not going to look at all of them today. 240 00:11:14,655 --> 00:11:16,768 We're going to focus primarily on views.py-- 241 00:11:16,768 --> 00:11:19,560 we'll see why in a moment-- which is going to be the file that lets 242 00:11:19,560 --> 00:11:22,380 us describe what it is the user sees when they visit 243 00:11:22,380 --> 00:11:24,390 a particular route, for example, where we can 244 00:11:24,390 --> 00:11:26,382 decide what gets rendered to the user. 245 00:11:26,382 --> 00:11:29,340 And the other files, we'll take a look at over the course of this term, 246 00:11:29,340 --> 00:11:33,130 taking a look at what additional features Django has to offer. 247 00:11:33,130 --> 00:11:35,220 So we've now created this hello app that we would 248 00:11:35,220 --> 00:11:37,720 like to install into this project. 249 00:11:37,720 --> 00:11:39,450 And in order to install it, we need to go 250 00:11:39,450 --> 00:11:42,430 into the settings for this particular Django project. 251 00:11:42,430 --> 00:11:45,060 So if I go into the lecture3 directory and open up 252 00:11:45,060 --> 00:11:49,950 settings.py, what I'll see here is a big file all created for me by Django. 253 00:11:49,950 --> 00:11:51,790 I didn't write any of this code. 254 00:11:51,790 --> 00:11:55,560 But this is all just the default configuration for a Django project. 255 00:11:55,560 --> 00:11:58,380 And if you scroll down, somewhere there you'll see a variable 256 00:11:58,380 --> 00:12:02,190 called INSTALLED_APPS, which is where Django configures what 257 00:12:02,190 --> 00:12:04,413 apps are installed on this project. 258 00:12:04,413 --> 00:12:06,330 And there are a whole bunch of individual apps 259 00:12:06,330 --> 00:12:09,025 that are installed by default that are just created by Django. 260 00:12:09,025 --> 00:12:12,150 One, for example, manages sessions, which we'll take a look at a little bit 261 00:12:12,150 --> 00:12:13,300 later. 262 00:12:13,300 --> 00:12:16,890 But if I want to add my new app that I've just created called hello 263 00:12:16,890 --> 00:12:19,110 to these installed apps, I'll go ahead and just 264 00:12:19,110 --> 00:12:22,800 add to this list of strings, this INSTALLED_APPS variable, 265 00:12:22,800 --> 00:12:24,290 I'll just add hello. 266 00:12:24,290 --> 00:12:29,010 And so now hello is going to be an installed app on this particular Django 267 00:12:29,010 --> 00:12:30,510 project. 268 00:12:30,510 --> 00:12:33,690 So now what I'd like to do is actually make this app, 269 00:12:33,690 --> 00:12:36,480 this hello app do something and display something 270 00:12:36,480 --> 00:12:39,160 when I try and visit a particular route, for example. 271 00:12:39,160 --> 00:12:41,110 So how can I go about doing that? 272 00:12:41,110 --> 00:12:43,620 Well, let's go ahead and go into the hello directory. 273 00:12:43,620 --> 00:12:47,980 And let's take a look at views.py inside of my hello app. 274 00:12:47,980 --> 00:12:50,400 So here is the default file, again, from views.py. 275 00:12:50,400 --> 00:12:52,290 I see comment that's given to me by default 276 00:12:52,290 --> 00:12:54,360 that says, create your views here. 277 00:12:54,360 --> 00:12:58,810 And you can think of each view as something the user might want to see. 278 00:12:58,810 --> 00:13:01,010 So let's go ahead and just create a default view. 279 00:13:01,010 --> 00:13:04,960 In order to create a view in Django, we're going to define a function. 280 00:13:04,960 --> 00:13:07,340 So this function I'll just call index. 281 00:13:07,340 --> 00:13:11,810 This function, by convention, takes as argument a request argument. 282 00:13:11,810 --> 00:13:13,560 And this is going to be an argument that's 283 00:13:13,560 --> 00:13:17,490 going to represent the HTTP request that the user made in order 284 00:13:17,490 --> 00:13:19,065 to access our web server. 285 00:13:19,065 --> 00:13:20,940 So if we want information about that request, 286 00:13:20,940 --> 00:13:22,860 we can look inside of this requested object 287 00:13:22,860 --> 00:13:24,450 to get access to some other data. 288 00:13:24,450 --> 00:13:27,360 And we'll take a look at that momentarily too. 289 00:13:27,360 --> 00:13:29,710 But now, what would I like for this request to do? 290 00:13:29,710 --> 00:13:32,460 Well, what I want to do for now is, let's just do something simple 291 00:13:32,460 --> 00:13:39,970 and return an HttpResponse of, "Hello, world!," for example. 292 00:13:39,970 --> 00:13:43,750 Now, HttpResponse is a special class created by Django. 293 00:13:43,750 --> 00:13:46,690 And so if I want to use it, I'll generally need to import it. 294 00:13:46,690 --> 00:13:48,370 And so Django is going to give us a lot of features 295 00:13:48,370 --> 00:13:50,453 that we might want to use in our web applications, 296 00:13:50,453 --> 00:13:53,590 but if we want to use them, we generally have to import them first. 297 00:13:53,590 --> 00:13:55,465 And it just so happens-- and I only know this 298 00:13:55,465 --> 00:13:58,690 from reading Django's documentation-- that I can include a line like this, 299 00:13:58,690 --> 00:14:06,280 from django.http import HttpResponse, to say I would like to import the ability 300 00:14:06,280 --> 00:14:08,260 to give an HTTP response. 301 00:14:08,260 --> 00:14:11,560 And now what I have is a function representing my view. 302 00:14:11,560 --> 00:14:13,280 This function is called index. 303 00:14:13,280 --> 00:14:16,000 And what the index function does is it just returns 304 00:14:16,000 --> 00:14:20,140 an HTTP response of, "Hello, world!" 305 00:14:20,140 --> 00:14:21,790 And so this is the response. 306 00:14:21,790 --> 00:14:24,160 But now we need to tell the app, like, when should you 307 00:14:24,160 --> 00:14:26,380 actually return this response? 308 00:14:26,380 --> 00:14:29,090 What URL is the user going to visit? 309 00:14:29,090 --> 00:14:32,800 And this is where we now begin to create some URL configuration, some sort 310 00:14:32,800 --> 00:14:37,060 of setting to tell Django when a particular URL is visited, 311 00:14:37,060 --> 00:14:41,800 then this function should be run in order to return that particular HTTP 312 00:14:41,800 --> 00:14:43,070 response. 313 00:14:43,070 --> 00:14:44,177 So how to do that-- 314 00:14:44,177 --> 00:14:46,510 well, in order to do that, we're going to need to create 315 00:14:46,510 --> 00:14:50,240 a urls.py file for this particular app. 316 00:14:50,240 --> 00:14:54,070 So Django has one urls.py file that works for the entire project. 317 00:14:54,070 --> 00:14:58,030 But oftentimes we'll have each app contain its own urls.py file just 318 00:14:58,030 --> 00:15:00,612 for the sake of separating things out into different places, 319 00:15:00,612 --> 00:15:03,070 such that if we have multiple different apps, each of which 320 00:15:03,070 --> 00:15:06,760 is doing something independent, we can have each of those individual apps 321 00:15:06,760 --> 00:15:10,300 have its own urls.py file to control what URLs 322 00:15:10,300 --> 00:15:13,520 are available for that particular app. 323 00:15:13,520 --> 00:15:16,870 So I'll go ahead and go into the hello directory. 324 00:15:16,870 --> 00:15:22,990 And I'll create a new file inside of the hello directory called you urls.py. 325 00:15:22,990 --> 00:15:27,940 And what urls.py needs is, it needs to define a variable called urlpatterns, 326 00:15:27,940 --> 00:15:31,240 which will be a list of all of the allowable URLs that can 327 00:15:31,240 --> 00:15:33,730 be accessed for this particular app. 328 00:15:33,730 --> 00:15:40,600 And the way you create a URL is by first importing from django.urls import path. 329 00:15:40,600 --> 00:15:42,280 And let's create our first URL. 330 00:15:42,280 --> 00:15:42,948 I'll say path. 331 00:15:42,948 --> 00:15:44,740 And then the first argument here, I'm going 332 00:15:44,740 --> 00:15:48,133 to give the empty string to mean no additional argument-- 333 00:15:48,133 --> 00:15:50,800 and we'll see what that means in just a moment-- meaning nothing 334 00:15:50,800 --> 00:15:52,790 at the end of the route. 335 00:15:52,790 --> 00:15:56,050 And then the second argument to path is what view should 336 00:15:56,050 --> 00:15:58,810 be rendered when this URL is visited. 337 00:15:58,810 --> 00:16:00,970 And so if I want to render my index view-- 338 00:16:00,970 --> 00:16:04,900 recall that in views.py over here, I have this index function-- 339 00:16:04,900 --> 00:16:09,130 then what I want to render when someone visits this URL, the empty URL, 340 00:16:09,130 --> 00:16:13,050 is going to be views.index. 341 00:16:13,050 --> 00:16:17,420 Views represents views.py, that file where I've defined all of my views. 342 00:16:17,420 --> 00:16:20,740 And an index just so happens to be the name of the function 343 00:16:20,740 --> 00:16:25,610 that I want to call when someone visits this URL, for example. 344 00:16:25,610 --> 00:16:29,170 And then you can optionally provide a path with a string name . 345 00:16:29,170 --> 00:16:32,560 To represent this URL I'm going to give this a name of index. 346 00:16:32,560 --> 00:16:34,390 We'll see why this can be useful later on, 347 00:16:34,390 --> 00:16:37,810 but the idea is, giving a name to a particular URL pattern 348 00:16:37,810 --> 00:16:40,675 makes it easy to reference it from other parts of the application. 349 00:16:40,675 --> 00:16:42,550 So later when we might want to link to things 350 00:16:42,550 --> 00:16:45,717 or have forms that are submitting to different parts of the web application, 351 00:16:45,717 --> 00:16:48,670 giving a name to a path can be a useful tool. 352 00:16:48,670 --> 00:16:51,760 In order to use views, last line I need to add to urls.py 353 00:16:51,760 --> 00:16:55,940 is to say from dot, in other words, from the current directory, 354 00:16:55,940 --> 00:16:58,540 go ahead and import views. 355 00:16:58,540 --> 00:17:01,810 Anytime I'm using a variable name like views, I'm using that name, 356 00:17:01,810 --> 00:17:03,400 I need to import it from somewhere. 357 00:17:03,400 --> 00:17:06,220 And it just so happens that views.py and urls.py 358 00:17:06,220 --> 00:17:08,210 are located in the same directory. 359 00:17:08,210 --> 00:17:11,980 So I can just say from dot import views to import all of that 360 00:17:11,980 --> 00:17:15,569 into this particular file. 361 00:17:15,569 --> 00:17:19,440 So this now is the urls.py file for this application. 362 00:17:19,440 --> 00:17:22,880 But the last step in order to get all of this working for the very first time 363 00:17:22,880 --> 00:17:27,849 is to go back into the lecture3 directory and open up urls.py here. 364 00:17:27,849 --> 00:17:30,880 This is the urls.py file for the entire project, 365 00:17:30,880 --> 00:17:34,002 for all of the apps that might be contained within this project. 366 00:17:34,002 --> 00:17:36,460 And it just so happens that there is one path already given 367 00:17:36,460 --> 00:17:40,330 to us by default called admin which runs a default Django application called 368 00:17:40,330 --> 00:17:41,560 the admin application. 369 00:17:41,560 --> 00:17:43,520 We'll see more about that in the next lecture. 370 00:17:43,520 --> 00:17:45,973 But for now, I'd like to add my own route here 371 00:17:45,973 --> 00:17:47,890 to say that I would like for a particular path 372 00:17:47,890 --> 00:17:52,520 to lead to not the admin app, but the hello app that I have just now created. 373 00:17:52,520 --> 00:17:55,740 So I'll go ahead and give this a path of hello. 374 00:17:55,740 --> 00:17:58,240 And what I'd like to do is say that, after you've 375 00:17:58,240 --> 00:18:02,200 included the path hello, go ahead and include all of the URLs 376 00:18:02,200 --> 00:18:05,560 from the urls.py my hello application. 377 00:18:05,560 --> 00:18:09,920 I'd basically like to link these two URL configuration files together. 378 00:18:09,920 --> 00:18:14,830 And so the command to do that is include hello.urls. 379 00:18:14,830 --> 00:18:19,450 Inside of the hello module, get at the urls file there. 380 00:18:19,450 --> 00:18:22,740 And that is now what I would like to add as a URL pattern. 381 00:18:22,740 --> 00:18:26,770 And in order to do this, I also need to import include since I'm 382 00:18:26,770 --> 00:18:30,100 using include inside of URL patterns. 383 00:18:30,100 --> 00:18:32,380 So that was a lot of steps just to get things started. 384 00:18:32,380 --> 00:18:35,110 But just to get a high-level overview of how this is all working, 385 00:18:35,110 --> 00:18:37,930 I have a Django project called lecture3. 386 00:18:37,930 --> 00:18:40,510 And inside of lecture3, there is a URLs file 387 00:18:40,510 --> 00:18:42,950 that decides what URLs I can access. 388 00:18:42,950 --> 00:18:47,020 I can access /admin, which takes me to the admin application which is created 389 00:18:47,020 --> 00:18:47,620 by Django. 390 00:18:47,620 --> 00:18:49,460 We don't have to worry about that just yet. 391 00:18:49,460 --> 00:18:53,620 But now I've just added that you can go to /hello to go to the hello 392 00:18:53,620 --> 00:18:54,610 application. 393 00:18:54,610 --> 00:18:56,830 And when you do that, I'm telling Django to look 394 00:18:56,830 --> 00:19:00,070 at the urls.py inside of the hello directory 395 00:19:00,070 --> 00:19:03,220 to figure out what additional URLs I can get to from there. 396 00:19:03,220 --> 00:19:05,710 So this is one master urls.py file that might 397 00:19:05,710 --> 00:19:10,900 connect to multiple different other URL configurations that exist as well. 398 00:19:10,900 --> 00:19:15,280 Then inside of the urls.py for my app, for the hello app, 399 00:19:15,280 --> 00:19:17,620 I've said that when someone visits just the default 400 00:19:17,620 --> 00:19:22,180 route on this particular application, go ahead and just run the index 401 00:19:22,180 --> 00:19:24,950 function that is one of my views. 402 00:19:24,950 --> 00:19:27,500 So all that's to say, now that that is done, 403 00:19:27,500 --> 00:19:29,470 I should be able to go back into my terminal 404 00:19:29,470 --> 00:19:35,360 and run python manage.py runserver to actually start up this web server. 405 00:19:35,360 --> 00:19:39,010 And now I can go back to this URL, but instead of just going to that default 406 00:19:39,010 --> 00:19:43,840 URL, I'll go ahead and go to that URL /hello. 407 00:19:43,840 --> 00:19:48,340 And when I go to /hello, what I see is hello world. 408 00:19:48,340 --> 00:19:51,310 So what was happening there is that I typed in a URL. 409 00:19:51,310 --> 00:19:53,110 That URL went to Django. 410 00:19:53,110 --> 00:19:56,980 And Django looked at that URL and looked at my urls.py file and said, you know, 411 00:19:56,980 --> 00:20:00,670 anytime something starts with /hello, that belongs to the hello app. 412 00:20:00,670 --> 00:20:02,440 And inside of our hello configuration, we 413 00:20:02,440 --> 00:20:04,600 said that when we go to the default route, 414 00:20:04,600 --> 00:20:07,310 we should run the index function. 415 00:20:07,310 --> 00:20:10,360 And the index function returns this "Hello, world!" 416 00:20:10,360 --> 00:20:11,836 response. 417 00:20:11,836 --> 00:20:13,750 And now we can begin to change that response. 418 00:20:13,750 --> 00:20:18,370 We can begin to adjust what it is that views.py is actually doing. 419 00:20:18,370 --> 00:20:19,960 Right now it says "Hello, world." 420 00:20:19,960 --> 00:20:21,835 But if I want to change it to just say, like, 421 00:20:21,835 --> 00:20:24,550 hello, for example, I can just edit it. 422 00:20:24,550 --> 00:20:28,900 Now say the index function just returns and HTTP response of "Hello!," 423 00:20:28,900 --> 00:20:30,310 for example. 424 00:20:30,310 --> 00:20:32,110 And so now I have to refresh this page. 425 00:20:32,110 --> 00:20:33,970 Now it just says "Hello!" 426 00:20:33,970 --> 00:20:35,740 So I can edit my files. 427 00:20:35,740 --> 00:20:38,260 When I do, Django notices that a change has been made. 428 00:20:38,260 --> 00:20:42,370 And it will re-update the server with my latest code such that now I can visit 429 00:20:42,370 --> 00:20:43,330 /hello. 430 00:20:43,330 --> 00:20:46,030 And I can see that an HTTP response of "Hello!" 431 00:20:46,030 --> 00:20:48,667 is what ultimately comes back to me. 432 00:20:48,667 --> 00:20:51,250 So now let's take a look at the idea that, in addition to just 433 00:20:51,250 --> 00:20:55,610 having one view inside of views.py, I can have as many views as I want. 434 00:20:55,610 --> 00:20:59,800 I can create additional functions that each return different responses. 435 00:20:59,800 --> 00:21:03,523 Maybe I'd like a route that says hello to me, for example. 436 00:21:03,523 --> 00:21:05,440 So let me define a new function that I'll call 437 00:21:05,440 --> 00:21:08,500 brian that accepts a request argument. 438 00:21:08,500 --> 00:21:15,490 And this function is just going to return HttpResponse "Hello, Brian!," 439 00:21:15,490 --> 00:21:17,110 for example. 440 00:21:17,110 --> 00:21:20,740 And now I need to associate this new view that I've created with the URL. 441 00:21:20,740 --> 00:21:25,310 So I'll go ahead and go back to urls.py inside of my hello directory. 442 00:21:25,310 --> 00:21:28,660 And so I have a default route represented by the empty string 443 00:21:28,660 --> 00:21:30,730 that loads the index function. 444 00:21:30,730 --> 00:21:34,240 But I can add to this, just add to this list, add a new path, 445 00:21:34,240 --> 00:21:37,060 where if I type brian into the URL instead, 446 00:21:37,060 --> 00:21:38,770 that will load the brian function. 447 00:21:38,770 --> 00:21:43,340 And I'll go ahead and give that a name as well. 448 00:21:43,340 --> 00:21:47,080 So now if I refresh the page-- and I'm still on just a /hello route. 449 00:21:47,080 --> 00:21:49,340 And that just displays "Hello!," for example. 450 00:21:49,340 --> 00:21:53,230 But if I go to /brian, for example, now I see "Hello, Brian!" 451 00:21:53,230 --> 00:21:57,590 I have two different URLs, one that is just /hello with nothing after it, 452 00:21:57,590 --> 00:22:01,150 which was that empty string, and one which was /hello with brian following 453 00:22:01,150 --> 00:22:06,493 it that loads a different view function that returns a different HTTP response. 454 00:22:06,493 --> 00:22:08,410 And so we could imagine continuing to do this. 455 00:22:08,410 --> 00:22:11,440 I might want a web application that has a number of different URLs 456 00:22:11,440 --> 00:22:12,400 that I can visit. 457 00:22:12,400 --> 00:22:14,800 So I could go ahead and add, like, a david function, 458 00:22:14,800 --> 00:22:22,480 for example, that returns in HttpResponse of "Hello, David!" 459 00:22:22,480 --> 00:22:25,090 And then again, inside of my URL configuration, 460 00:22:25,090 --> 00:22:29,950 I would say, go ahead and give me another route called david that loads 461 00:22:29,950 --> 00:22:32,310 the david function. 462 00:22:32,310 --> 00:22:36,660 Each time, I'm giving it a URL, so what comes after the /hello. 463 00:22:36,660 --> 00:22:41,640 I'm giving it a function to run, the david function inside of views.py, 464 00:22:41,640 --> 00:22:46,340 and giving it a name just to make it easier to reference a little bit later. 465 00:22:46,340 --> 00:22:50,820 And so now I have /hello/brian, and now I have /hello/david, 466 00:22:50,820 --> 00:22:54,690 each of which displays a different HTTP response. 467 00:22:54,690 --> 00:22:56,910 So you can imagine starting to use this feature 468 00:22:56,910 --> 00:23:00,020 to begin to build a web application that has a number of different routes. 469 00:23:00,020 --> 00:23:01,980 It has different routes to do different things. 470 00:23:01,980 --> 00:23:05,340 And many web applications are parameterized 471 00:23:05,340 --> 00:23:07,980 by what it is it's actually in the URL. 472 00:23:07,980 --> 00:23:12,130 So for example, on Twitter, you can go to a Twitter.com slash someone's user 473 00:23:12,130 --> 00:23:14,670 name to see all of that user's tweets, for example. 474 00:23:14,670 --> 00:23:16,920 Or GitHub, a service we've taken a look at now, 475 00:23:16,920 --> 00:23:19,520 you can go to GitHub.com slash your GitHub user 476 00:23:19,520 --> 00:23:23,700 name to be able to see all of the repositories for a particular user, 477 00:23:23,700 --> 00:23:25,028 for example. 478 00:23:25,028 --> 00:23:28,320 So if you think about how that might be implemented if either of these services 479 00:23:28,320 --> 00:23:33,210 were using Django, they might have a urls.py file or something like it 480 00:23:33,210 --> 00:23:35,310 that is just defining a whole bunch of URLs 481 00:23:35,310 --> 00:23:38,490 and saying what route should be associated with them when 482 00:23:38,490 --> 00:23:40,560 someone visits that page. 483 00:23:40,560 --> 00:23:44,310 But you can imagine ultimately that this is probably going to start to get 484 00:23:44,310 --> 00:23:46,770 tedious, that if I want to say hello to anyone, 485 00:23:46,770 --> 00:23:50,640 if I want to not only support /hello, /brian, and /david, 486 00:23:50,640 --> 00:23:54,210 but I also want to support arbitrary names like /harry, or /ron, 487 00:23:54,210 --> 00:23:57,690 or /hermione, well then it would seem that I need to create a whole bunch 488 00:23:57,690 --> 00:24:01,150 of different functions, each of which says hello to a different person, 489 00:24:01,150 --> 00:24:04,260 and then create a whole bunch of different URLs to be able to do that 490 00:24:04,260 --> 00:24:05,440 as well. 491 00:24:05,440 --> 00:24:09,180 But it turns out that what I can do is create URL patterns that 492 00:24:09,180 --> 00:24:11,580 have placeholders, in effect, things that 493 00:24:11,580 --> 00:24:16,980 allow me to parameterize the path via certain path converters, so to speak. 494 00:24:16,980 --> 00:24:18,970 So what does that actually mean? 495 00:24:18,970 --> 00:24:21,050 Well, let's instead of creating functions 496 00:24:21,050 --> 00:24:23,160 that just say "Hello, Brian!" and "Hello, David!," 497 00:24:23,160 --> 00:24:25,930 let me create a new function that's just called greet, 498 00:24:25,930 --> 00:24:30,480 for example, that also takes a request, but takes an additional parameter. 499 00:24:30,480 --> 00:24:34,680 It takes a parameter like someone's name, for example. 500 00:24:34,680 --> 00:24:36,930 And this greet function is going to return 501 00:24:36,930 --> 00:24:43,440 an HttpResponse of "Hello comma," and then I'll go ahead and plug in the name 502 00:24:43,440 --> 00:24:44,240 here. 503 00:24:44,240 --> 00:24:46,410 And I'll need to add an f to the beginning of the string to indicate 504 00:24:46,410 --> 00:24:47,760 that it's a formatted string-- 505 00:24:47,760 --> 00:24:49,440 this is just Python syntax-- 506 00:24:49,440 --> 00:24:52,660 to say that I would like to say hello, not just to anyone, 507 00:24:52,660 --> 00:24:55,610 but to whatever the value of the name variable happens to be. 508 00:24:55,610 --> 00:24:59,850 I'd like to substitute the name variable into that greet function. 509 00:24:59,850 --> 00:25:04,370 And then what I can do is, inside of you urls.py, 510 00:25:04,370 --> 00:25:09,060 I'll go ahead and add another path, but instead of saying a name like Brian, 511 00:25:09,060 --> 00:25:14,070 or David, or Harry, for example here, I'm going to say in angled brackets 512 00:25:14,070 --> 00:25:17,350 str:name. 513 00:25:17,350 --> 00:25:21,180 And when someone visits that route, let's go to the views.greet function. 514 00:25:21,180 --> 00:25:24,830 And the name for this route will be greet. 515 00:25:24,830 --> 00:25:28,980 And so what's going on here is we have a fundamentally different kind of path. 516 00:25:28,980 --> 00:25:32,430 Rather than prescribing exactly what the URL should look like, like, nothing 517 00:25:32,430 --> 00:25:34,972 after the end of the route, or Brian at the end of the route, 518 00:25:34,972 --> 00:25:38,010 or David at the end of the route, this route here on line seven 519 00:25:38,010 --> 00:25:41,532 is saying this route code be any string that we're going 520 00:25:41,532 --> 00:25:42,990 to give a variable name of name to. 521 00:25:42,990 --> 00:25:45,580 But you could replace a name with something else entirely. 522 00:25:45,580 --> 00:25:46,530 And this could be any string. 523 00:25:46,530 --> 00:25:47,822 So it could be Brian, or David. 524 00:25:47,822 --> 00:25:49,330 Or it could be any other name. 525 00:25:49,330 --> 00:25:51,780 And when that happens, we'll call the greet function. 526 00:25:51,780 --> 00:25:56,010 And when that function is called, it will pass in this argument, this name, 527 00:25:56,010 --> 00:25:58,690 as a parameter to that function. 528 00:25:58,690 --> 00:26:00,990 So now I'd be able to create a custom route that 529 00:26:00,990 --> 00:26:03,750 allows me to specify any string and figure out 530 00:26:03,750 --> 00:26:06,270 how to deal with that appropriately. 531 00:26:06,270 --> 00:26:09,240 So now if I visit /hello/harry, for example, 532 00:26:09,240 --> 00:26:12,520 not a route that I explicitly created, but that is a string-- 533 00:26:12,520 --> 00:26:13,740 harry is a string. 534 00:26:13,740 --> 00:26:14,450 I press Return. 535 00:26:14,450 --> 00:26:15,660 It says, "Hello, harry!" 536 00:26:15,660 --> 00:26:20,820 I can go to /hello/ron and say "Hello, ron!," and /hermione to see "Hello, 537 00:26:20,820 --> 00:26:22,410 hermione!" as well. 538 00:26:22,410 --> 00:26:23,160 And you know what? 539 00:26:23,160 --> 00:26:25,770 Maybe just for good measure, I'd like to capitalize this name, 540 00:26:25,770 --> 00:26:27,660 like Hermione, for example. 541 00:26:27,660 --> 00:26:31,290 Well, the way to do that is that I can add arbitrary Python logic. 542 00:26:31,290 --> 00:26:33,470 It turns out that with any Python string, 543 00:26:33,470 --> 00:26:37,560 there is a function or method on that string called capitalize that I can say 544 00:26:37,560 --> 00:26:41,520 is just dot capitalize. 545 00:26:41,520 --> 00:26:43,530 And if I can do it in Python, then Django 546 00:26:43,530 --> 00:26:46,470 allows me to incorporate it into the response that I'm giving back. 547 00:26:46,470 --> 00:26:49,260 So I'm now using Python to take the name, 548 00:26:49,260 --> 00:26:52,290 capitalize it, and use that in the response that 549 00:26:52,290 --> 00:26:54,460 gets sent back to the user. 550 00:26:54,460 --> 00:26:57,660 So now /hermione returns "Hello, Hermione!" name with a capital H, 551 00:26:57,660 --> 00:27:01,440 likewise for "Hello, Ron!," likewise for "Hello, Harry!" 552 00:27:01,440 --> 00:27:05,580 I've now been able to add a route that takes an HTTP request 553 00:27:05,580 --> 00:27:08,940 as well as a parameter, that name, whatever was in the URL, 554 00:27:08,940 --> 00:27:14,280 and return an HTTP response that just says hello to that person. 555 00:27:14,280 --> 00:27:18,250 So these HTTP responses can be any HTML content. 556 00:27:18,250 --> 00:27:20,590 Right now we're just using text, but you can imagine 557 00:27:20,590 --> 00:27:23,210 adding lists or tables to this as well. 558 00:27:23,210 --> 00:27:27,370 But you might imagine that if I have to include an entire HTML page just 559 00:27:27,370 --> 00:27:30,648 inside these double quotes, that that's going to get unwieldy very quickly. 560 00:27:30,648 --> 00:27:32,440 Very quickly we're going to find that there 561 00:27:32,440 --> 00:27:36,933 is a lot of HTML to just go in a single string inside of a Python program. 562 00:27:36,933 --> 00:27:39,100 And if we think back to the principles and the ideas 563 00:27:39,100 --> 00:27:41,142 that we've been looking at in this course so far, 564 00:27:41,142 --> 00:27:45,070 we've generally tried to separate out different parts of our application 565 00:27:45,070 --> 00:27:45,820 wherever possible. 566 00:27:45,820 --> 00:27:47,528 And there is a lot of value in that, one, 567 00:27:47,528 --> 00:27:49,670 in just keeping things clean, two in making sure 568 00:27:49,670 --> 00:27:51,170 that people are able to collaborate. 569 00:27:51,170 --> 00:27:54,370 If you want one person working on the Python logic and one person working 570 00:27:54,370 --> 00:27:57,280 on the HTML, you'd rather they not step on each other's toes 571 00:27:57,280 --> 00:27:58,460 as they're working. 572 00:27:58,460 --> 00:28:03,490 And so what we can do in Django as well is separate the response, the HTML, 573 00:28:03,490 --> 00:28:07,450 from the actual Python code here as well. 574 00:28:07,450 --> 00:28:11,920 And so the way we can do that is, instead of returning an HTTP response, 575 00:28:11,920 --> 00:28:12,760 I can instead-- 576 00:28:12,760 --> 00:28:14,800 let's say for this default route, instead 577 00:28:14,800 --> 00:28:19,870 of returning an HTTP response of hello, let me go ahead and render. 578 00:28:19,870 --> 00:28:23,920 And when I render something I need to pass in the HTTP request. 579 00:28:23,920 --> 00:28:27,320 But I'll also pass in the name of a template. 580 00:28:27,320 --> 00:28:29,740 And I'll go ahead and call this template hello/index.html. 581 00:28:29,740 --> 00:28:33,280 582 00:28:33,280 --> 00:28:35,510 So if I don't want to render just a string, 583 00:28:35,510 --> 00:28:40,120 but I want to render an entire HTML file, I can call this render function, 584 00:28:40,120 --> 00:28:43,960 pass in the request, but then also pass in the name of a template. 585 00:28:43,960 --> 00:28:46,660 And now all I need to do is create that template. 586 00:28:46,660 --> 00:28:49,795 So let me go ahead, and inside of the hello directory, 587 00:28:49,795 --> 00:28:53,680 I'll create a new folder called templates. 588 00:28:53,680 --> 00:28:56,770 Inside of that template, I'll create a folder called hello, 589 00:28:56,770 --> 00:29:00,760 because the template name here is hello/index.html. 590 00:29:00,760 --> 00:29:04,030 So I need to create a folder called hello, inside of which is a file 591 00:29:04,030 --> 00:29:05,650 called index.html. 592 00:29:05,650 --> 00:29:09,160 I could have just called it index.html without the hello, but the reason 593 00:29:09,160 --> 00:29:12,490 we often want to prefix our templates with a directory name 594 00:29:12,490 --> 00:29:15,070 is to so-called namespace them, to make sure 595 00:29:15,070 --> 00:29:17,452 that if we have multiple different index.html files 596 00:29:17,452 --> 00:29:20,660 in multiple different apps, to make sure they don't conflict with each other. 597 00:29:20,660 --> 00:29:24,550 So Django best practice is to use hello/index.html, 598 00:29:24,550 --> 00:29:26,170 or whatever your app name is. 599 00:29:26,170 --> 00:29:32,470 And inside of that hello directory, I'll then create a file called index.html. 600 00:29:32,470 --> 00:29:37,520 And this index.html can contain any HTML content, same as we've seen before. 601 00:29:37,520 --> 00:29:42,730 So I could add that doc type, the HTML tag, a title of hello. 602 00:29:42,730 --> 00:29:47,060 And maybe inside the body I'll have an h1 that says, "Hello, World!," 603 00:29:47,060 --> 00:29:47,930 for example. 604 00:29:47,930 --> 00:29:51,770 So I can have an entire HTML page just like this. 605 00:29:51,770 --> 00:29:55,870 And now when I visit the default route of my web application by going back 606 00:29:55,870 --> 00:29:59,440 into my web browser and just going to /hello, for example, 607 00:29:59,440 --> 00:30:04,860 now I see that HTML that I've defined inside of index.html. 608 00:30:04,860 --> 00:30:09,070 It's a big h1 that just says, "Hello, world!" 609 00:30:09,070 --> 00:30:13,030 And it turns out that these HTML pages, these templates that I can render using 610 00:30:13,030 --> 00:30:16,430 Django are parameterizable as well. 611 00:30:16,430 --> 00:30:22,570 Maybe I want to implement hello/hermione as some sort of HTML page, an HTML page 612 00:30:22,570 --> 00:30:24,760 that says, "Hello, Hermione!" 613 00:30:24,760 --> 00:30:28,110 And using HTML out of the box, you can't really do stuff like this. 614 00:30:28,110 --> 00:30:32,000 HTML is a markup language, not so much a programming language, 615 00:30:32,000 --> 00:30:35,440 which means it doesn't, by default, have support for things like a variable 616 00:30:35,440 --> 00:30:37,270 to represent someone's name. 617 00:30:37,270 --> 00:30:41,800 But using Django's ability to take an HTML page and treat it like a template 618 00:30:41,800 --> 00:30:45,670 that we can render, Django has added its own templating language, 619 00:30:45,670 --> 00:30:48,780 so to speak, on top of the existing HTML. 620 00:30:48,780 --> 00:30:52,780 And I can take advantage of that to be able to render an HTML page that 621 00:30:52,780 --> 00:30:55,810 actually has a variable inside of it, or a condition inside of it, 622 00:30:55,810 --> 00:31:00,560 or a loop inside of it, as we'll see in just a moment as well. 623 00:31:00,560 --> 00:31:02,650 So let's go ahead and go back to the views.py. 624 00:31:02,650 --> 00:31:04,510 And the function that I would like to change 625 00:31:04,510 --> 00:31:09,130 is this greet function here that right now is just returning an HTTP response, 626 00:31:09,130 --> 00:31:13,130 but I'd like to render an entire page, for example. 627 00:31:13,130 --> 00:31:14,320 So what might I render? 628 00:31:14,320 --> 00:31:17,890 Well, let's render a template called hello/greet.html. 629 00:31:17,890 --> 00:31:21,250 630 00:31:21,250 --> 00:31:24,830 And then this render function can take an optional third argument 631 00:31:24,830 --> 00:31:26,770 which is called the context. 632 00:31:26,770 --> 00:31:29,440 And the context is all of the information 633 00:31:29,440 --> 00:31:33,410 that I would like to provide to the template, all the variables, 634 00:31:33,410 --> 00:31:36,000 for example, that I want the template to have access to. 635 00:31:36,000 --> 00:31:38,500 And so one thing I might want my template to have access to, 636 00:31:38,500 --> 00:31:42,260 for instance, is something like a name. 637 00:31:42,260 --> 00:31:46,760 And so this is a Python dictionary, just a sequence of key value pairs. 638 00:31:46,760 --> 00:31:49,510 And this name might be associated with what value? 639 00:31:49,510 --> 00:31:50,710 So name is the key. 640 00:31:50,710 --> 00:31:52,990 The value I want to be name.capitalize. 641 00:31:52,990 --> 00:31:55,610 642 00:31:55,610 --> 00:31:59,510 And so what's going on here is that now when I render this template, 643 00:31:59,510 --> 00:32:04,060 this template hello/greet.html, I'm providing that template with some 644 00:32:04,060 --> 00:32:07,600 additional content, some additional information as represented by this 645 00:32:07,600 --> 00:32:11,460 dictionary here, where I'm providing information with a key of name 646 00:32:11,460 --> 00:32:15,040 and effectively giving this template access to a variable called name. 647 00:32:15,040 --> 00:32:18,520 And its value is whatever name.capitalize 648 00:32:18,520 --> 00:32:22,850 is equal to, where this name here is the argument to the function greet. 649 00:32:22,850 --> 00:32:24,550 So now I can return this template. 650 00:32:24,550 --> 00:32:30,060 And inside of greet.html, I can use this variable called name. 651 00:32:30,060 --> 00:32:32,210 So how do I go about doing that? 652 00:32:32,210 --> 00:32:35,200 Well, let's first create that greet.html file. 653 00:32:35,200 --> 00:32:41,260 So inside of the template/hello directory, I already have index.html. 654 00:32:41,260 --> 00:32:45,910 I'll go ahead and create a new file called greet.html. 655 00:32:45,910 --> 00:32:48,890 And inside of greet.html, I'll go ahead and write some HTML. 656 00:32:48,890 --> 00:32:52,910 657 00:32:52,910 --> 00:32:55,770 And inside the body of the HTML, whereas before I 658 00:32:55,770 --> 00:32:59,022 might have said something like "Hello, world," 659 00:32:59,022 --> 00:33:00,480 I don't want to say hello to world. 660 00:33:00,480 --> 00:33:05,100 I just want to say hello to whoever the name is, whatever is contained inside 661 00:33:05,100 --> 00:33:08,470 of that variable called name. 662 00:33:08,470 --> 00:33:10,450 So how might they go about doing that? 663 00:33:10,450 --> 00:33:14,160 Well, what I can do is use these double curly braces. 664 00:33:14,160 --> 00:33:17,520 And double curly braces are part of the Django templating language 665 00:33:17,520 --> 00:33:21,420 that allows me to say, I would like to plug in the value of the variable 666 00:33:21,420 --> 00:33:25,320 into this particular position inside of my template. 667 00:33:25,320 --> 00:33:28,830 And so if I include the word name here, then what I'm saying here 668 00:33:28,830 --> 00:33:33,510 is that when you render this greet.html template, inside the body of the page, 669 00:33:33,510 --> 00:33:37,320 I would like for there to be an h1, a big heading that just says "hello 670 00:33:37,320 --> 00:33:38,340 comma." 671 00:33:38,340 --> 00:33:40,140 And then inside these double curly braces, 672 00:33:40,140 --> 00:33:43,477 I'm saying plug-in the value of the variable name there. 673 00:33:43,477 --> 00:33:45,810 If name is Hermione, it's going to be "Hello, Hermoine!" 674 00:33:45,810 --> 00:33:47,768 If it's Harry, it's going to be "Hello, Harry!" 675 00:33:47,768 --> 00:33:50,820 If it's David, it's going to be "Hello, David!". 676 00:33:50,820 --> 00:33:53,190 So let's go ahead and give this a try now. 677 00:33:53,190 --> 00:33:56,990 Now when I go to /hello/harry, for example, 678 00:33:56,990 --> 00:33:59,840 I now see a big h1 tag that says "Hello, Harry!" 679 00:33:59,840 --> 00:34:05,612 If I go to /hello/Ron, I see a big h1 that says "Hello, Ron!" 680 00:34:05,612 --> 00:34:07,320 And the reason why all this is happening, 681 00:34:07,320 --> 00:34:09,510 the way we're getting to that final position 682 00:34:09,510 --> 00:34:11,880 is because you urls says that when I have 683 00:34:11,880 --> 00:34:14,770 a URL that just is a string like someone's name, 684 00:34:14,770 --> 00:34:16,489 we'll call the greet function. 685 00:34:16,489 --> 00:34:21,030 The greet function inside of views.py is taking the name as an argument, 686 00:34:21,030 --> 00:34:25,020 rendering the greet.html template, and passing in that name 687 00:34:25,020 --> 00:34:29,070 as part of the context, part of what the template has access to. 688 00:34:29,070 --> 00:34:33,650 And inside of greet.html, here is the actual HTML file. 689 00:34:33,650 --> 00:34:37,710 Here is where we then plug in the value of name. 690 00:34:37,710 --> 00:34:40,840 So it's a lot happening across a lot of different files. 691 00:34:40,840 --> 00:34:43,260 But the reason for all of these various different files 692 00:34:43,260 --> 00:34:45,960 is to help keep things separate, to have one file that 693 00:34:45,960 --> 00:34:49,739 is just responsible for URLs and directing people to what should 694 00:34:49,739 --> 00:34:51,510 happen when those URLs are clicked. 695 00:34:51,510 --> 00:34:56,159 Then we have one file, views.py, that is entirely responsible for deciding, , 696 00:34:56,159 --> 00:34:58,920 on this particular view what template should be rendered, 697 00:34:58,920 --> 00:35:01,570 what information should be passed in this context. 698 00:35:01,570 --> 00:35:05,130 And then I separately have a file for each of my HTML templates 699 00:35:05,130 --> 00:35:08,300 that are saying what does the page actually look like. 700 00:35:08,300 --> 00:35:12,030 And so if you start to begin to think about the separation of components 701 00:35:12,030 --> 00:35:15,720 inside of a web application, it can help to make the structure of a Django 702 00:35:15,720 --> 00:35:18,950 application a little bit clearer. 703 00:35:18,950 --> 00:35:23,290 So we've now been able to use the Django templating language to put variables 704 00:35:23,290 --> 00:35:25,420 inside of our HTML templates, to be able to create 705 00:35:25,420 --> 00:35:27,430 an infinite number of different routes where 706 00:35:27,430 --> 00:35:31,150 I can visit slash hello slash some name in order 707 00:35:31,150 --> 00:35:33,730 to display hello to that person's name. 708 00:35:33,730 --> 00:35:36,730 But the Django templating language is even more powerful than just that. 709 00:35:36,730 --> 00:35:39,760 There are a lot of additional features that the templating language 710 00:35:39,760 --> 00:35:41,640 is going to give us access to. 711 00:35:41,640 --> 00:35:43,850 And we'll take a look at a couple of those now. 712 00:35:43,850 --> 00:35:47,322 And to start I'm going to introduce you to a website you may be familiar with. 713 00:35:47,322 --> 00:35:50,030 I consider it to be one of the simplest websites on the internet. 714 00:35:50,030 --> 00:35:52,300 It's a real website called isitchristmas.com. 715 00:35:52,300 --> 00:35:57,710 And if I visit isitchristmas.com and press Return, 716 00:35:57,710 --> 00:35:58,960 here is what the website says. 717 00:35:58,960 --> 00:36:00,550 The website says, no. 718 00:36:00,550 --> 00:36:01,662 It is not Christmas. 719 00:36:01,662 --> 00:36:03,370 And you just have to take my word for it. 720 00:36:03,370 --> 00:36:05,470 If you visit this website on Christmas day, 721 00:36:05,470 --> 00:36:09,320 you go to isitchristmas.com, it will instead say, yes. 722 00:36:09,320 --> 00:36:11,230 And so this website is very simple. 723 00:36:11,230 --> 00:36:14,710 You might imagine that it's really just an HTML page that probably contains 724 00:36:14,710 --> 00:36:17,740 a big heading that, in this case, just says the word "no," 725 00:36:17,740 --> 00:36:20,050 but on Christmas day, just as the word "yes." 726 00:36:20,050 --> 00:36:22,510 Now, how might a page like this be implemented? 727 00:36:22,510 --> 00:36:25,030 Well, one way you could imagine is that on Christmas day 728 00:36:25,030 --> 00:36:30,010 whoever maintains the website goes into the HTML and changes no to yes, 729 00:36:30,010 --> 00:36:32,980 and then afterwards changes the yes back to a no. 730 00:36:32,980 --> 00:36:34,930 But we can be a little bit cleverer about this 731 00:36:34,930 --> 00:36:37,240 if we realize now that we have the ability 732 00:36:37,240 --> 00:36:41,440 to use Python logic to be able to use logic like a condition 733 00:36:41,440 --> 00:36:45,102 to be able to decide how a web page is ultimately going to be rendered. 734 00:36:45,102 --> 00:36:47,560 For instance, the condition might be something simple like, 735 00:36:47,560 --> 00:36:52,392 if today's date is Christmas, then render yes, else render no. 736 00:36:52,392 --> 00:36:54,100 And so we're going to use Django and take 737 00:36:54,100 --> 00:36:57,250 a look at some of the features of Django's templating syntax to be 738 00:36:57,250 --> 00:36:58,770 able to create a website like this. 739 00:36:58,770 --> 00:37:00,520 We're not going to create Is It Christmas. 740 00:37:00,520 --> 00:37:02,353 We're going to create Is It New Year's, that 741 00:37:02,353 --> 00:37:06,040 checks if the current date is January 1 or not. 742 00:37:06,040 --> 00:37:07,662 This is going to be a separate app. 743 00:37:07,662 --> 00:37:09,370 It's sort of distinct from our hello app. 744 00:37:09,370 --> 00:37:11,320 The hello app just says hello to people. 745 00:37:11,320 --> 00:37:13,690 The new year app, for example, is just going 746 00:37:13,690 --> 00:37:17,590 to see whether or not it happens to be New Year's, for example. 747 00:37:17,590 --> 00:37:20,030 So I will now go ahead and create a new app. 748 00:37:20,030 --> 00:37:24,460 And I can do that by some writing python manage.py startapp. 749 00:37:24,460 --> 00:37:26,710 And I'll call the app newyear, for example. 750 00:37:26,710 --> 00:37:29,570 I'd like to create a new app called newyear. 751 00:37:29,570 --> 00:37:30,820 I've now created that new app. 752 00:37:30,820 --> 00:37:32,950 If I type ls, you'll see that not only do 753 00:37:32,950 --> 00:37:35,680 I have a hello directory that represents the hello app, 754 00:37:35,680 --> 00:37:38,620 I now have this newyear directory that represents the fact 755 00:37:38,620 --> 00:37:41,830 that this is a new app called newyear. 756 00:37:41,830 --> 00:37:47,080 As with before, I'll need to go into settings.py inside of my project 757 00:37:47,080 --> 00:37:51,610 directory and add newyear as an installed app. 758 00:37:51,610 --> 00:37:55,300 This is now a new app that exists in my web application, 759 00:37:55,300 --> 00:37:58,313 so it too now needs to be a new installed app. 760 00:37:58,313 --> 00:37:59,980 What else do we need to do for new apps? 761 00:37:59,980 --> 00:38:05,830 Well, I need to go into urls.py for lecture3. 762 00:38:05,830 --> 00:38:09,730 And just as before, I had a path saying when I go to /hello, 763 00:38:09,730 --> 00:38:12,460 then you should go to all the URLs for the hello app, 764 00:38:12,460 --> 00:38:17,500 let me add a new path that says that if I go to my application /newyear, 765 00:38:17,500 --> 00:38:21,830 well then you should go to all of URLs for the newyear file, 766 00:38:21,830 --> 00:38:28,300 go to newyear.urls, representing the urls.py file inside of the newyear app. 767 00:38:28,300 --> 00:38:31,990 Now, that file isn't given to us by default, so we need to create it. 768 00:38:31,990 --> 00:38:40,360 I need to go into my newyear folder and create a new file called urls.py. 769 00:38:40,360 --> 00:38:46,870 And inside of that file, I'll do from django.urls import path, from . 770 00:38:46,870 --> 00:38:51,480 import views, and then define some urlpattern, same as before, 771 00:38:51,480 --> 00:38:55,330 where I'll just have a single path that loads the index function inside 772 00:38:55,330 --> 00:38:56,770 of views.py. 773 00:38:56,770 --> 00:38:59,500 And it has a name of index. 774 00:38:59,500 --> 00:39:02,020 So again, just mirroring the type of structure 775 00:39:02,020 --> 00:39:04,310 that we've already seen before in the hello app-- 776 00:39:04,310 --> 00:39:07,840 this app is just going to have a single route, the empty route, that 777 00:39:07,840 --> 00:39:10,720 loads the index function. 778 00:39:10,720 --> 00:39:14,120 Now all that's left to do is to actually create that index function. 779 00:39:14,120 --> 00:39:16,090 So we go into views.py. 780 00:39:16,090 --> 00:39:18,940 And here now I'll define an index function that 781 00:39:18,940 --> 00:39:21,670 takes an HTTP request as an argument. 782 00:39:21,670 --> 00:39:24,160 And now inside of this index function, I would 783 00:39:24,160 --> 00:39:28,588 like to add some logic that checks whether or not it's January 1. 784 00:39:28,588 --> 00:39:30,630 And so how might I go about doing that in Python? 785 00:39:30,630 --> 00:39:34,432 Well, it turns out that there is a date time module in Python. 786 00:39:34,432 --> 00:39:37,390 And you can learn about this just by reading up about its documentation 787 00:39:37,390 --> 00:39:38,860 to figure out how it works. 788 00:39:38,860 --> 00:39:42,640 And the date time module gives me access to things about the date and time, 789 00:39:42,640 --> 00:39:43,833 for instance. 790 00:39:43,833 --> 00:39:46,750 And I can play around with the date time module outside of Django just 791 00:39:46,750 --> 00:39:48,340 to get a feel for how it works. 792 00:39:48,340 --> 00:39:51,520 If I just type python into the interpreter-- 793 00:39:51,520 --> 00:39:55,210 into my command line, rather, what I get is the Python interpreter that 794 00:39:55,210 --> 00:39:58,780 lets me just write Python code just to experiment, and test, 795 00:39:58,780 --> 00:40:01,960 and see what the result of running this Python code would be. 796 00:40:01,960 --> 00:40:05,900 So I can try, like, import datetime, for example. 797 00:40:05,900 --> 00:40:12,160 And let me create a new variable called now, which is datetime.datetime.now. 798 00:40:12,160 --> 00:40:14,590 It just so happens that inside of the datetime module 799 00:40:14,590 --> 00:40:17,500 is a function called datetime.now that gets 800 00:40:17,500 --> 00:40:20,540 the current date and time, for example. 801 00:40:20,540 --> 00:40:24,400 And so inside of now, I have access to variables like now.year, 802 00:40:24,400 --> 00:40:27,310 for example, which tells me the year, now.month, 803 00:40:27,310 --> 00:40:30,440 now.day that gives me information about the year, 804 00:40:30,440 --> 00:40:33,880 the month, and the day of the current timestamp based on getting 805 00:40:33,880 --> 00:40:36,520 whatever the current time is right now. 806 00:40:36,520 --> 00:40:38,410 And given that I have this information. 807 00:40:38,410 --> 00:40:40,360 You can imagine us constructing a Boolean 808 00:40:40,360 --> 00:40:45,640 condition to check whether or not today is in fact the new year. 809 00:40:45,640 --> 00:40:52,510 And that condition might look like now.month equals 1 and now.day equals 810 00:40:52,510 --> 00:40:53,010 1. 811 00:40:53,010 --> 00:40:57,440 And I can press Return and see that, OK, the result of this condition is false. 812 00:40:57,440 --> 00:41:01,800 It is not true that both the month the day are true-- 813 00:41:01,800 --> 00:41:03,340 or are 1, rather. 814 00:41:03,340 --> 00:41:05,500 And so using that sort of condition, I can now 815 00:41:05,500 --> 00:41:09,700 take that and adapt it into my Django view 816 00:41:09,700 --> 00:41:13,730 that I'm using to try and render whether or not it's the new year or not. 817 00:41:13,730 --> 00:41:15,560 So how might I go about doing that? 818 00:41:15,560 --> 00:41:17,650 Well, what I'd like for my index function to do 819 00:41:17,650 --> 00:41:20,840 is return render a template. 820 00:41:20,840 --> 00:41:25,330 The template will be called newyear/index.html. 821 00:41:25,330 --> 00:41:28,510 And then what context what I would like to provide to this template? 822 00:41:28,510 --> 00:41:30,730 What information, what variables do I want 823 00:41:30,730 --> 00:41:32,763 for this template to have access to? 824 00:41:32,763 --> 00:41:34,930 Well, I want it to have access to a variable that'll 825 00:41:34,930 --> 00:41:37,260 just be called newyear. 826 00:41:37,260 --> 00:41:40,210 And in order to access that, I need access to the current date. 827 00:41:40,210 --> 00:41:47,370 So to do that, at the top of my file, I'll go ahead and import datetime. 828 00:41:47,370 --> 00:41:50,110 And inside of my index function, let me give myself 829 00:41:50,110 --> 00:41:55,180 a variable called now, which is equal to datetime.datetime.now. 830 00:41:55,180 --> 00:41:58,950 And this newyear variable that I'm passing in to my template 831 00:41:58,950 --> 00:42:07,370 will be equal to now.month equals 1 and now.day equals 1. 832 00:42:07,370 --> 00:42:09,820 So if it is the case that after I get the current date 833 00:42:09,820 --> 00:42:12,640 and time by running datetime.datetime.now, 834 00:42:12,640 --> 00:42:14,530 saving the result inside of this variable 835 00:42:14,530 --> 00:42:18,590 called now, if both and the day are equal to 1, 836 00:42:18,590 --> 00:42:20,590 then the value of this new year variable is 837 00:42:20,590 --> 00:42:23,320 going to be true when the template gets access to it. 838 00:42:23,320 --> 00:42:25,990 Otherwise, as it is the case today and on most days, 839 00:42:25,990 --> 00:42:29,360 the value of that variable is going to be false. 840 00:42:29,360 --> 00:42:32,710 Now all that remains for us to do is to actually create this template, 841 00:42:32,710 --> 00:42:38,110 newyear/index.html, and have that template somehow use this newyear 842 00:42:38,110 --> 00:42:41,600 variable in some interesting or meaningful way. 843 00:42:41,600 --> 00:42:42,733 So how do we do that? 844 00:42:42,733 --> 00:42:45,400 Well, if you recall what we did with the hello application, when 845 00:42:45,400 --> 00:42:49,510 we want a template, then inside of our application, this newyear app, 846 00:42:49,510 --> 00:42:53,830 we'll need a new folder that we'll call templates, 847 00:42:53,830 --> 00:42:59,260 inside of which I'll create a new folder called newyear. 848 00:42:59,260 --> 00:43:05,320 And inside of that, I'll create a new file that will be called index.html. 849 00:43:05,320 --> 00:43:09,160 Here will be the index.html file for this new year application. 850 00:43:09,160 --> 00:43:10,930 The structure will be very similar. 851 00:43:10,930 --> 00:43:15,040 I'll give it a title of Is it New Year's? 852 00:43:15,040 --> 00:43:18,670 And inside the body, now here is where the interesting logic is. 853 00:43:18,670 --> 00:43:21,850 Sometimes I might want a big h1 that says, YES. 854 00:43:21,850 --> 00:43:24,700 Other times, I might want a big h1 that says, NO. 855 00:43:24,700 --> 00:43:28,510 And what I need to do is conditionally decide when to say yes 856 00:43:28,510 --> 00:43:30,230 and when to say no. 857 00:43:30,230 --> 00:43:32,630 And inside Django's templating language-- 858 00:43:32,630 --> 00:43:35,440 and again, you'd only know this by reading Django's documentation-- 859 00:43:35,440 --> 00:43:39,580 just as we used double curly braces to say plug in the value of a variable 860 00:43:39,580 --> 00:43:44,260 here, the syntax for logic inside of a Django template, like conditions, 861 00:43:44,260 --> 00:43:46,880 is curly brace percent sign. 862 00:43:46,880 --> 00:43:50,050 So we use curly brace percent sign, some logical statement, and then 863 00:43:50,050 --> 00:43:53,490 percent sine curly brace to include any kind of logic. 864 00:43:53,490 --> 00:43:56,042 And the logic in this case is an if statement. 865 00:43:56,042 --> 00:43:57,250 And this is very Python-like. 866 00:43:57,250 --> 00:44:00,790 We'll say if newyear-- 867 00:44:00,790 --> 00:44:04,470 newyear is the name of that variable that I passed into this template. 868 00:44:04,470 --> 00:44:09,610 And if it is a new year, then I want to display an h1 that just says, YES. 869 00:44:09,610 --> 00:44:14,830 And then else, also inside of curly braces and percent signs, 870 00:44:14,830 --> 00:44:18,430 I want a big h1 that just says, NO. 871 00:44:18,430 --> 00:44:20,050 So if it's the new year, then yes. 872 00:44:20,050 --> 00:44:21,160 Else, no. 873 00:44:21,160 --> 00:44:24,490 And then Django also requires me to give an endif tag 874 00:44:24,490 --> 00:44:26,470 to say this is the end of the if statement. 875 00:44:26,470 --> 00:44:30,490 Unlike Python itself that uses indentation to denote when the if 876 00:44:30,490 --> 00:44:32,440 statement is beginning and ending, in Django, 877 00:44:32,440 --> 00:44:34,840 the indentation is optional inside of the template. 878 00:44:34,840 --> 00:44:37,210 But in order to distinguish when the if statement is 879 00:44:37,210 --> 00:44:39,610 happening from when the if statement is over, 880 00:44:39,610 --> 00:44:42,650 we need this endif tag at the very end. 881 00:44:42,650 --> 00:44:45,460 So this here is a condition inside of our Django template. 882 00:44:45,460 --> 00:44:50,230 We're saying, if a particular variable is true, then render this in the HTML. 883 00:44:50,230 --> 00:44:52,390 And else render something else. 884 00:44:52,390 --> 00:44:54,580 If it's the new year, say, yes, it's the new year. 885 00:44:54,580 --> 00:44:57,820 Otherwise say, no, it is not the new year. 886 00:44:57,820 --> 00:44:59,260 So we can try this out. 887 00:44:59,260 --> 00:45:01,780 Right now we're on the route /hello/ron. 888 00:45:01,780 --> 00:45:07,000 If I instead, not go to /hello, but go to /newyear, for example, 889 00:45:07,000 --> 00:45:08,330 this site can't be reached. 890 00:45:08,330 --> 00:45:09,830 OK, so why did that happen? 891 00:45:09,830 --> 00:45:12,280 127.0.0.1 refused to connect. 892 00:45:12,280 --> 00:45:15,640 So what must be happening is, for some reason, my web server isn't running. 893 00:45:15,640 --> 00:45:18,223 And it turns out that, in order to create the new application, 894 00:45:18,223 --> 00:45:19,550 I had stopped my web server. 895 00:45:19,550 --> 00:45:21,820 And so if ever that happens, to start it up again, 896 00:45:21,820 --> 00:45:25,750 we can just rerun python manage.py runserver 897 00:45:25,750 --> 00:45:28,930 to say I would like to now start up this web application again. 898 00:45:28,930 --> 00:45:32,320 And all right, it looks like now I have another error. 899 00:45:32,320 --> 00:45:34,300 There is some sort of syntax error. 900 00:45:34,300 --> 00:45:38,920 And the syntax error is a newyear/views.py So let me go ahead 901 00:45:38,920 --> 00:45:42,810 and go back into views.py and see if I can spot where the syntax error is. 902 00:45:42,810 --> 00:45:45,490 And all right, it looks like render is a function. 903 00:45:45,490 --> 00:45:48,190 Function arguments need to be enclosed in parentheses. 904 00:45:48,190 --> 00:45:50,187 And while I have a starting parenthesis here, 905 00:45:50,187 --> 00:45:51,770 I had forgotten a closing parenthesis. 906 00:45:51,770 --> 00:45:52,812 So I'll go ahead and add. 907 00:45:52,812 --> 00:45:55,910 That and now that will complete the render function. 908 00:45:55,910 --> 00:45:59,080 And now I should be able to load /newyear. 909 00:45:59,080 --> 00:46:02,670 And indeed what I see is that, no, it is not the new year. 910 00:46:02,670 --> 00:46:05,860 And so what happened is we ran some Python logic, calculated 911 00:46:05,860 --> 00:46:09,250 the current date and time, checked to see is the current month 1, 912 00:46:09,250 --> 00:46:10,600 is the current day 1. 913 00:46:10,600 --> 00:46:13,900 And if that's not true, then we're here displaying, no. 914 00:46:13,900 --> 00:46:16,570 And if we look at the actual HTML of this page, 915 00:46:16,570 --> 00:46:19,930 like, what HTML is actually making up this particular page, 916 00:46:19,930 --> 00:46:22,090 which I can do by right clicking or Control 917 00:46:22,090 --> 00:46:25,490 clicking and just clicking View Page Source-- 918 00:46:25,490 --> 00:46:27,850 this will show me the HTML of this page. 919 00:46:27,850 --> 00:46:30,970 So this is the HTML that came back from the web server 920 00:46:30,970 --> 00:46:33,730 that my web browser, Chrome in this case, is rendering. 921 00:46:33,730 --> 00:46:37,120 And what you can see is it looks very similar to the index.html template we 922 00:46:37,120 --> 00:46:40,030 wrote before, but it only contains no. 923 00:46:40,030 --> 00:46:41,875 It doesn't contain any of that if logic. 924 00:46:41,875 --> 00:46:43,000 It doesn't contain the yes. 925 00:46:43,000 --> 00:46:46,930 It just contains the HTML content that we wanted 926 00:46:46,930 --> 00:46:49,708 to respond with back to the user. 927 00:46:49,708 --> 00:46:51,750 And so you might imagine based on this that would 928 00:46:51,750 --> 00:46:55,360 Django is really doing is taking the index.html template 929 00:46:55,360 --> 00:46:57,822 and then manipulating it based on the input we get, 930 00:46:57,822 --> 00:46:59,530 based on whether or not it's the new year 931 00:46:59,530 --> 00:47:02,230 or not, to say that, if it's not the new year, 932 00:47:02,230 --> 00:47:05,830 then all we should do inside the body is display an h1 that says, no. 933 00:47:05,830 --> 00:47:08,410 And the user when they get it, they don't see the conditions. 934 00:47:08,410 --> 00:47:11,118 They don't see there was another branch that could've been taken. 935 00:47:11,118 --> 00:47:13,630 They only see the final result of rendering 936 00:47:13,630 --> 00:47:17,500 the template with whatever variables, and logic, and conditions happened 937 00:47:17,500 --> 00:47:19,820 to be inside of that rendering process. 938 00:47:19,820 --> 00:47:24,010 So when the user sees it, all they see is the word "no." 939 00:47:24,010 --> 00:47:26,320 And if we were to run this program on New Year's Day, 940 00:47:26,320 --> 00:47:27,480 it would indeed say, yes. 941 00:47:27,480 --> 00:47:29,980 And we can test it just to see what it would be like if that 942 00:47:29,980 --> 00:47:31,720 were the case by cheating a little bit. 943 00:47:31,720 --> 00:47:35,260 Instead of now.month equals 1 and now.day equals 1, let me just go ahead 944 00:47:35,260 --> 00:47:39,220 and replace this condition with newyear is equal to true. 945 00:47:39,220 --> 00:47:43,030 Just to test it, let's try passing in the value true as the value of newyear 946 00:47:43,030 --> 00:47:44,860 and see what happens instead. 947 00:47:44,860 --> 00:47:47,590 And now when I run this page, now it does say yes, 948 00:47:47,590 --> 00:47:49,857 as we might expect it to on New Year's. 949 00:47:49,857 --> 00:47:51,940 And so that can be a nice way of just testing what 950 00:47:51,940 --> 00:47:54,910 would happen if you were to replace a particular variable 951 00:47:54,910 --> 00:47:56,260 with a particular value. 952 00:47:56,260 --> 00:47:58,360 We can substitute inside the context just 953 00:47:58,360 --> 00:48:03,640 for development purposes what value we would like that variable to take on. 954 00:48:03,640 --> 00:48:06,250 Now we've rendered HTML, but we haven't really 955 00:48:06,250 --> 00:48:08,620 added any styling to this website just yet. 956 00:48:08,620 --> 00:48:11,890 The real isitchristmas.com has the text centered a little bit. 957 00:48:11,890 --> 00:48:15,460 It's in sans serif font instead of having the little serifs or the glyphs 958 00:48:15,460 --> 00:48:17,170 at the edge of each of the characters. 959 00:48:17,170 --> 00:48:21,768 So maybe I'd like to add some custom CSS to this file as well. 960 00:48:21,768 --> 00:48:23,560 And we could do this conventionally the way 961 00:48:23,560 --> 00:48:26,260 we've seen it done before by just including a CSS file. 962 00:48:26,260 --> 00:48:28,090 And that is what we're still going to do. 963 00:48:28,090 --> 00:48:31,120 But Django has a special build system for dealing 964 00:48:31,120 --> 00:48:34,960 with what are called static files, files that aren't going to change. 965 00:48:34,960 --> 00:48:37,810 The HTML of this page, that's not a static file, 966 00:48:37,810 --> 00:48:40,780 because it changes depending on whether it's New Year's or not. 967 00:48:40,780 --> 00:48:42,700 If I visit it on New Year's, it says, yes. 968 00:48:42,700 --> 00:48:45,530 If I visit it when it's not New Year's, it says, no. 969 00:48:45,530 --> 00:48:47,560 And so this is a dynamic page. 970 00:48:47,560 --> 00:48:50,690 But static files, files that don't change, like our CSS-- 971 00:48:50,690 --> 00:48:53,930 the CSS doesn't change whether it's New Year's or not. 972 00:48:53,930 --> 00:48:57,760 And because that file is unchanging, Django calls it a static file. 973 00:48:57,760 --> 00:49:00,760 And it means that Django can be a little bit cleverer about it. 974 00:49:00,760 --> 00:49:02,740 If you start to think about projects of scale, 975 00:49:02,740 --> 00:49:05,230 you might store your static file somewhere separate 976 00:49:05,230 --> 00:49:08,980 just in some place that makes it easy to access, where they're cached for faster 977 00:49:08,980 --> 00:49:10,018 reads later on. 978 00:49:10,018 --> 00:49:12,310 We'll talk about that a little more later in the course 979 00:49:12,310 --> 00:49:14,560 as we start to delve into topics like scalability, 980 00:49:14,560 --> 00:49:17,320 as you begin to build larger web applications on the internet. 981 00:49:17,320 --> 00:49:19,510 But long story short, Django contains a lot 982 00:49:19,510 --> 00:49:23,050 of features that make it easy for us to deal with static files, files 983 00:49:23,050 --> 00:49:26,230 that don't change like CSS files. 984 00:49:26,230 --> 00:49:29,770 And generally the way that we'll add static files is, 985 00:49:29,770 --> 00:49:32,350 inside of the newyear folder, in addition to having 986 00:49:32,350 --> 00:49:39,250 a templates/newyear/index.html, we'll also create a new folder called static 987 00:49:39,250 --> 00:49:42,880 that will contain all of the static files that we would like to include 988 00:49:42,880 --> 00:49:45,820 inside of this application as well. 989 00:49:45,820 --> 00:49:49,360 Inside of static, I'll create a new folder called newyear, 990 00:49:49,360 --> 00:49:53,590 inside of which is a new file called styles.css. 991 00:49:53,590 --> 00:49:57,012 And so inside of styles.css, I can now write all of the same CSS 992 00:49:57,012 --> 00:49:58,720 that I would want to have written before. 993 00:49:58,720 --> 00:50:05,740 So maybe I want all my h1s to have a font family of sans serif, a font size 994 00:50:05,740 --> 00:50:09,790 of 90 pixels, maybe, and I want the text alignment 995 00:50:09,790 --> 00:50:13,420 to be centered, just a couple of CSS properties and values, same 996 00:50:13,420 --> 00:50:15,580 as we've already seen, just to say I would like 997 00:50:15,580 --> 00:50:19,670 to give h1 tags this particular style. 998 00:50:19,670 --> 00:50:24,930 Now what's left to do inside of index.html is, at the top of our page, 999 00:50:24,930 --> 00:50:29,600 I'll go ahead and add a command that says load static to mean go ahead 1000 00:50:29,600 --> 00:50:34,450 and load static files for this particular HTML page. 1001 00:50:34,450 --> 00:50:39,080 And now I'll go ahead and link a style sheet. 1002 00:50:39,080 --> 00:50:44,300 You'll recall that this type of command inside the head section of my web page 1003 00:50:44,300 --> 00:50:47,750 where I would like to link some particular URL as a style sheet 1004 00:50:47,750 --> 00:50:50,490 is how I add CSS to a page. 1005 00:50:50,490 --> 00:50:53,210 But what I'm going to include in here as the link is, 1006 00:50:53,210 --> 00:50:57,680 I'm going to include, rather than hardcoding a URL, which I could do, 1007 00:50:57,680 --> 00:51:00,050 Django best practice says that, instead, let's just 1008 00:51:00,050 --> 00:51:02,150 say it's going to be a static file. 1009 00:51:02,150 --> 00:51:05,230 And the static file is called newyear/styles.css. 1010 00:51:05,230 --> 00:51:07,870 1011 00:51:07,870 --> 00:51:10,670 So I'm not specifying exactly what the URL is, 1012 00:51:10,670 --> 00:51:14,270 but I'm saying it is a static file that is inside of a newyear folder 1013 00:51:14,270 --> 00:51:15,890 called styles.css. 1014 00:51:15,890 --> 00:51:19,730 And Django is going to figure out what that URL ought to be. 1015 00:51:19,730 --> 00:51:22,550 And this is often better than hardcoding a specific URL, 1016 00:51:22,550 --> 00:51:25,580 because maybe you might imagine in larger web applications, where 1017 00:51:25,580 --> 00:51:27,410 you're static files are might change. 1018 00:51:27,410 --> 00:51:29,660 You might move your static files to a different domain 1019 00:51:29,660 --> 00:51:30,840 or to a different route. 1020 00:51:30,840 --> 00:51:33,920 And so in order to deal with that, this static keyword 1021 00:51:33,920 --> 00:51:37,640 just means Django will figure out where your static files are located and will 1022 00:51:37,640 --> 00:51:40,970 replace this command here in the curly braces and percent 1023 00:51:40,970 --> 00:51:45,060 signs with the actual URL for this particular file. 1024 00:51:45,060 --> 00:51:48,470 So now that I've said that I want to link this particular static file, 1025 00:51:48,470 --> 00:51:51,830 styles.css, into this web page, what I might first 1026 00:51:51,830 --> 00:51:55,160 need to do just to let Django load static files is restart the server. 1027 00:51:55,160 --> 00:51:57,380 So you might sometimes need to Control C and then 1028 00:51:57,380 --> 00:52:00,170 go ahead and rerun python manage.py runserver. 1029 00:52:00,170 --> 00:52:01,610 That will rerun the server. 1030 00:52:01,610 --> 00:52:05,498 And now if I go back to the /newyear route inside my web browser, 1031 00:52:05,498 --> 00:52:08,540 now I see the style look a little more close to what I actually wanted it 1032 00:52:08,540 --> 00:52:13,070 to be, centered, sans serif, in a larger font that, here, just says, no. 1033 00:52:13,070 --> 00:52:16,050 And if we look at the underlying HTML of this page, 1034 00:52:16,050 --> 00:52:21,380 we see that Django has actually for us filled in what the static URL is 1035 00:52:21,380 --> 00:52:23,030 for these particular static files. 1036 00:52:23,030 --> 00:52:28,160 And by default, Django just uses /static/ and then whatever the files 1037 00:52:28,160 --> 00:52:29,282 happen to be. 1038 00:52:29,282 --> 00:52:31,490 And so anytime we're dealing with static files, files 1039 00:52:31,490 --> 00:52:34,640 that don't change like CSS files or JavaScript files, which 1040 00:52:34,640 --> 00:52:37,670 we'll take a look at later in the course, that's generally where they're 1041 00:52:37,670 --> 00:52:41,450 going to go, in some sort of static file that we're going to ultimately link 1042 00:52:41,450 --> 00:52:44,430 to this particular page. 1043 00:52:44,430 --> 00:52:47,460 So now we've seen a couple of examples of web applications 1044 00:52:47,460 --> 00:52:48,650 we can make using Django. 1045 00:52:48,650 --> 00:52:51,230 We've seen the hello app in Django that can parameterize 1046 00:52:51,230 --> 00:52:53,840 URLs, that, depending on the URL we visit, 1047 00:52:53,840 --> 00:52:57,290 can say hello to Brian, or hello to David, or Harry, or Ron, or Hermione. 1048 00:52:57,290 --> 00:53:00,770 And we've seen the ability to, using Django, add some conditions, 1049 00:53:00,770 --> 00:53:03,380 to be able to conditionally say, if something is true, 1050 00:53:03,380 --> 00:53:06,470 render this page, if something else is true, render another page. 1051 00:53:06,470 --> 00:53:09,560 Let's now use these features plus some additional features 1052 00:53:09,560 --> 00:53:13,430 to begin to build a more sophisticated web application, something like a to do 1053 00:53:13,430 --> 00:53:16,070 list, that maybe I want to web application that gives me 1054 00:53:16,070 --> 00:53:19,550 a list of tasks, that lets me add new tasks to my to do list, 1055 00:53:19,550 --> 00:53:23,120 and lets me view all of the tasks that are currently on my list. 1056 00:53:23,120 --> 00:53:26,780 And I'd like to build this up one step at a time. 1057 00:53:26,780 --> 00:53:28,580 So where can I start with this? 1058 00:53:28,580 --> 00:53:30,860 Well this, again, is going to be a new app. 1059 00:53:30,860 --> 00:53:33,620 Inside of my lecture3 project, I have two apps so far, 1060 00:53:33,620 --> 00:53:37,070 a hello app and an app for the newyear. 1061 00:53:37,070 --> 00:53:39,400 And now what I'd like to do is create a third app 1062 00:53:39,400 --> 00:53:41,000 that I'm just going to call tasks. 1063 00:53:41,000 --> 00:53:43,280 That is going to be my task management app that's all 1064 00:53:43,280 --> 00:53:45,920 under this umbrella lecture3 project. 1065 00:53:45,920 --> 00:53:50,900 So I'll go ahead and run python manage.py startapp tasks 1066 00:53:50,900 --> 00:53:54,140 to say I'd like to go ahead and create a new app called tasks. 1067 00:53:54,140 --> 00:53:57,770 Anytime I create a new app, a couple of steps I need to follow-- 1068 00:53:57,770 --> 00:54:03,560 recall that I need to go into settings.py inside of lecture3 1069 00:54:03,560 --> 00:54:06,260 to say that, in addition to installing hello and newyear, 1070 00:54:06,260 --> 00:54:10,960 I'd also like to install the tasks app, for example. 1071 00:54:10,960 --> 00:54:14,840 Then I need to go into urls.py inside of lecture3 1072 00:54:14,840 --> 00:54:16,910 to say that, in addition to hello and newyear, 1073 00:54:16,910 --> 00:54:21,470 I'd also like for us to include the URLs for tasks. 1074 00:54:21,470 --> 00:54:25,490 This urls is, again, the table of contents for my entire web application, 1075 00:54:25,490 --> 00:54:28,492 where in my web application, if I visit /hello, 1076 00:54:28,492 --> 00:54:30,200 then we go to the URLs for the hello app. 1077 00:54:30,200 --> 00:54:34,490 If I go to /tasks, then I go to the URLs for the tasks up, so on and so forth. 1078 00:54:34,490 --> 00:54:39,090 It tells me all of the different URLs that I now have access to. 1079 00:54:39,090 --> 00:54:41,840 And as with before, the URLs file isn't created 1080 00:54:41,840 --> 00:54:45,240 for me inside of my application. 1081 00:54:45,240 --> 00:54:49,490 So I will need to go into the tasks directory. 1082 00:54:49,490 --> 00:54:53,330 And inside of the task directory, I'll go ahead and create a new file 1083 00:54:53,330 --> 00:54:55,460 that I will call urls.py. 1084 00:54:55,460 --> 00:54:58,460 And the format of this is going to be very similar to what we've already 1085 00:54:58,460 --> 00:55:04,220 seen, from django.urls import path from . import views. 1086 00:55:04,220 --> 00:55:06,950 And now I define my urlpatterns, where I would 1087 00:55:06,950 --> 00:55:11,630 like a path that is just the empty string, the empty path for now, 1088 00:55:11,630 --> 00:55:17,730 that loads the index function whose name is index, for example. 1089 00:55:17,730 --> 00:55:21,560 So now let's actually write this index function inside of views.py. 1090 00:55:21,560 --> 00:55:25,940 What I'd like to do is define a function called index that accepts a request. 1091 00:55:25,940 --> 00:55:29,270 And what I'd like to do is render a page that 1092 00:55:29,270 --> 00:55:32,480 displays a list of all of my tasks. 1093 00:55:32,480 --> 00:55:34,580 So before we get to the idea of adding tasks, 1094 00:55:34,580 --> 00:55:37,880 let's just see if we can get our program to display a list of tasks, 1095 00:55:37,880 --> 00:55:38,990 for example. 1096 00:55:38,990 --> 00:55:42,060 So I'll, up at the top, create a global variable 1097 00:55:42,060 --> 00:55:44,400 that I'll just call tasks that the entire application is 1098 00:55:44,400 --> 00:55:45,650 going to have access to. 1099 00:55:45,650 --> 00:55:47,750 And I'll add three tasks to it. 1100 00:55:47,750 --> 00:55:51,650 For now, we'll just use foo, bar, and baz, just sort of nonsense names 1101 00:55:51,650 --> 00:55:54,380 that are just useful strings for testing purposes, 1102 00:55:54,380 --> 00:55:56,870 just placing those as sample tasks that I might 1103 00:55:56,870 --> 00:56:00,110 want to have inside of my application. 1104 00:56:00,110 --> 00:56:04,530 And now what I'd like to do is render a template. 1105 00:56:04,530 --> 00:56:07,295 The template I'd like to render will be called tasks/index.html. 1106 00:56:07,295 --> 00:56:09,980 1107 00:56:09,980 --> 00:56:12,680 And then I'll provide some context to it, some information 1108 00:56:12,680 --> 00:56:14,570 that index.html needs. 1109 00:56:14,570 --> 00:56:17,390 What information does index.html need? 1110 00:56:17,390 --> 00:56:22,730 Well, index.html needs access to all of my tasks. 1111 00:56:22,730 --> 00:56:24,003 And where are all of my tasks? 1112 00:56:24,003 --> 00:56:26,420 Well, they're inside of this variable that just so happens 1113 00:56:26,420 --> 00:56:28,610 to also be called tasks. 1114 00:56:28,610 --> 00:56:31,640 And this will be something we see quite often as a paradigm in Django, 1115 00:56:31,640 --> 00:56:34,970 where we see a key and a value that look like they have the same name. 1116 00:56:34,970 --> 00:56:38,280 The key distinction is, whatever is here on the right after the colon, 1117 00:56:38,280 --> 00:56:40,070 this is the value the variable takes on. 1118 00:56:40,070 --> 00:56:43,610 This is a Python variable, like this Python variable tasks here. 1119 00:56:43,610 --> 00:56:47,930 On the left, this key, this string tasks, that is the name of the variable 1120 00:56:47,930 --> 00:56:52,280 that the HTML template will have access to when Django is rendering it. 1121 00:56:52,280 --> 00:56:55,010 So Django has access to this variable name on the left that 1122 00:56:55,010 --> 00:56:57,240 has this value on the right. 1123 00:56:57,240 --> 00:57:00,620 So if you see this paradigm, that's ultimately what that's going to mean. 1124 00:57:00,620 --> 00:57:04,640 And now what I need to do is create this index.html file 1125 00:57:04,640 --> 00:57:08,360 and make it use this tasks variable somehow. 1126 00:57:08,360 --> 00:57:10,230 How might I to go about doing that? 1127 00:57:10,230 --> 00:57:12,950 Well, I can go back into my tasks directory, 1128 00:57:12,950 --> 00:57:15,200 create a new folder for my templates. 1129 00:57:15,200 --> 00:57:19,460 So I'll create a templates directory, inside of which is a tasks directory, 1130 00:57:19,460 --> 00:57:25,490 because the template that I'm rendering, again, is tasks/index.html. 1131 00:57:25,490 --> 00:57:29,960 And inside of tasks, I'll create a new file called index.html, inside 1132 00:57:29,960 --> 00:57:32,210 of which we'll include some HTML. 1133 00:57:32,210 --> 00:57:35,540 I'll include just the standard starting HTML. 1134 00:57:35,540 --> 00:57:37,460 The title will be Tasks. 1135 00:57:37,460 --> 00:57:40,190 And now we'll have the body of the HTML page, 1136 00:57:40,190 --> 00:57:44,910 that I want to display all the tasks inside of an unordered list, perhaps. 1137 00:57:44,910 --> 00:57:47,660 And if you recall from HTML, to create an unordered list, 1138 00:57:47,660 --> 00:57:50,990 it looks a little something like this. ul starts the unordered list. 1139 00:57:50,990 --> 00:57:56,240 And each list item is an li tag, like item one, and then item two, 1140 00:57:56,240 --> 00:57:59,720 and then item three. 1141 00:57:59,720 --> 00:58:02,180 Something like that gives me an unordered list 1142 00:58:02,180 --> 00:58:04,620 that has three elements inside of it. 1143 00:58:04,620 --> 00:58:07,370 But I don't want to do this now, because I don't want to hard code 1144 00:58:07,370 --> 00:58:11,060 or exactly specify what all of the tasks are going to be. 1145 00:58:11,060 --> 00:58:15,470 What I really want to do is loop over this tasks variable 1146 00:58:15,470 --> 00:58:18,560 here, looping over all of the tasks inside of it 1147 00:58:18,560 --> 00:58:22,520 and creating a list item for each one of those tasks. 1148 00:58:22,520 --> 00:58:28,460 And just as in before where we could use if inside of curly braces and percent 1149 00:58:28,460 --> 00:58:30,860 signs to use a condition, likewise we can 1150 00:58:30,860 --> 00:58:35,000 use for to say something like, for task in tasks, 1151 00:58:35,000 --> 00:58:38,360 I would like to display a list item. 1152 00:58:38,360 --> 00:58:42,110 And then using these double curly braces, I'm saying plug in the task 1153 00:58:42,110 --> 00:58:42,650 here. 1154 00:58:42,650 --> 00:58:44,990 In between these list item tasks, I would 1155 00:58:44,990 --> 00:58:49,250 like to plug in whatever the value of the task variable is. 1156 00:58:49,250 --> 00:58:53,012 And then I'll go ahead and end the for loop there. 1157 00:58:53,012 --> 00:58:55,970 So what I've done here is, rather than add a condition into a template, 1158 00:58:55,970 --> 00:58:57,970 which we've already seen, I now have the ability 1159 00:58:57,970 --> 00:59:00,470 to add a loop into an HTML template using 1160 00:59:00,470 --> 00:59:03,680 Django to say that, for task in tasks, I'd 1161 00:59:03,680 --> 00:59:08,310 like to loop over all of the elements inside of this sequence called tasks. 1162 00:59:08,310 --> 00:59:10,130 And for each one of those individual tasks, 1163 00:59:10,130 --> 00:59:14,810 I would like to display a list item in HTML that includes whatever 1164 00:59:14,810 --> 00:59:17,120 the value of the task happens to be. 1165 00:59:17,120 --> 00:59:19,940 An endfor, just like endif in the if statement, 1166 00:59:19,940 --> 00:59:23,280 endfor is going to end the for loop. 1167 00:59:23,280 --> 00:59:26,870 So this syntax now, rather than give me exactly the same number 1168 00:59:26,870 --> 00:59:30,740 of list items every time with exactly the same content, will be dynamic. 1169 00:59:30,740 --> 00:59:34,640 Whatever the value of tasks is, we will now see on this page 1170 00:59:34,640 --> 00:59:37,200 each one as a list item. 1171 00:59:37,200 --> 00:59:40,790 And we can test this right now if actually run this application 1172 00:59:40,790 --> 00:59:45,720 by going to python manage.py runserver. 1173 00:59:45,720 --> 00:59:48,560 I'll go ahead and go to this URL. 1174 00:59:48,560 --> 00:59:52,660 The default URL has no page, but if I go to /tasks, well, 1175 00:59:52,660 --> 00:59:53,660 then here is what I see. 1176 00:59:53,660 --> 00:59:58,040 I see an unordered list that has foo, bar, and baz, each one as a list item. 1177 00:59:58,040 --> 01:00:01,220 And if I view the page source to see what the actual HTML is, 1178 01:00:01,220 --> 01:00:02,570 here is what I see. 1179 01:00:02,570 --> 01:00:05,750 I only wrote one li tag inside of my template, 1180 01:00:05,750 --> 01:00:07,460 but because I put it inside of a for loop 1181 01:00:07,460 --> 01:00:11,330 that iterates over each of the tasks, what I ultimately get in the HTML that 1182 01:00:11,330 --> 01:00:15,350 comes back to the user is one list item for each of the elements 1183 01:00:15,350 --> 01:00:18,770 that was originally inside of that list. 1184 01:00:18,770 --> 01:00:21,000 So I now have the ability to loop over a list 1185 01:00:21,000 --> 01:00:24,090 in order to generate that list of tasks. 1186 01:00:24,090 --> 01:00:26,760 Of course, there is still no way to modify any of these tasks. 1187 01:00:26,760 --> 01:00:29,130 What we have here is just a fixed list of tasks 1188 01:00:29,130 --> 01:00:31,980 that just so happens to be rendering using a loop. 1189 01:00:31,980 --> 01:00:34,620 What I'd maybe like is another page that will 1190 01:00:34,620 --> 01:00:38,730 allow me to add in new tasks, a form, effectively, 1191 01:00:38,730 --> 01:00:43,960 that lets me type in a new task and press add in order to add a new task. 1192 01:00:43,960 --> 01:00:45,180 So let's do that. 1193 01:00:45,180 --> 01:00:51,480 Inside of views.py, I'll define a new function that I'm going to call add. 1194 01:00:51,480 --> 01:00:56,790 And would the add function is going to do is render tasks/add.html. 1195 01:00:56,790 --> 01:01:00,390 1196 01:01:00,390 --> 01:01:03,710 In order to know when to run the view, I need to give this view a URL. 1197 01:01:03,710 --> 01:01:07,830 So I go into urls.py and add a new path. 1198 01:01:07,830 --> 01:01:12,540 When I go to the add route, I want to go to the views.add function. 1199 01:01:12,540 --> 01:01:17,370 And I'll call this function add, for example. 1200 01:01:17,370 --> 01:01:23,870 And so now when I go to /task/add, it's going to call the function inside 1201 01:01:23,870 --> 01:01:25,138 of views. 1202 01:01:25,138 --> 01:01:26,930 And inside of views here, it's going to run 1203 01:01:26,930 --> 01:01:30,830 the function that will render add.html. 1204 01:01:30,830 --> 01:01:34,370 So let's go ahead and now write add.html. 1205 01:01:34,370 --> 01:01:35,685 We'll go into templates. 1206 01:01:35,685 --> 01:01:39,020 I'll create a new file called add.html. 1207 01:01:39,020 --> 01:01:39,770 And you know what? 1208 01:01:39,770 --> 01:01:43,820 Add.html syntax is very similar to the syntax from index.html 1209 01:01:43,820 --> 01:01:46,370 in terms of what the HTML content is. 1210 01:01:46,370 --> 01:01:49,860 So I'll go ahead and just copy this whole page from index.html, 1211 01:01:49,860 --> 01:01:51,770 paste it into add.html. 1212 01:01:51,770 --> 01:01:53,993 The only thing that's different is the body 1213 01:01:53,993 --> 01:01:56,160 of the page, where instead of an unordered list that 1214 01:01:56,160 --> 01:02:01,220 displays all the tasks, I'd instead like a form, where that form has 1215 01:02:01,220 --> 01:02:03,650 an input whose type is text-- 1216 01:02:03,650 --> 01:02:06,860 and maybe this input field has a name called task such 1217 01:02:06,860 --> 01:02:09,290 that I can access that input data later-- 1218 01:02:09,290 --> 01:02:12,363 and an input whose type is submit, for example. 1219 01:02:12,363 --> 01:02:14,780 And maybe I'll give it a big heading at the top that says, 1220 01:02:14,780 --> 01:02:19,180 like, add task, for example. 1221 01:02:19,180 --> 01:02:21,380 So now I have a new route that adds a task. 1222 01:02:21,380 --> 01:02:25,830 So my default /tasks route just displays a bulleted list of all the tasks. 1223 01:02:25,830 --> 01:02:31,040 And if I go to /tasks/add, here now is my form that gives me a place where I 1224 01:02:31,040 --> 01:02:33,110 can type in a task, press Submit. 1225 01:02:33,110 --> 01:02:35,660 That right now does nothing, but that ultimately, I hope, 1226 01:02:35,660 --> 01:02:38,870 will actually add a new task. 1227 01:02:38,870 --> 01:02:40,760 Of course, something I just did there should 1228 01:02:40,760 --> 01:02:43,160 strike us as not the best design. 1229 01:02:43,160 --> 01:02:46,810 In particular, the decision I made was because the general structure 1230 01:02:46,810 --> 01:02:48,740 of this HTML page is similar-- 1231 01:02:48,740 --> 01:02:50,030 it's got the HTML tag. 1232 01:02:50,030 --> 01:02:50,870 It's got a head. 1233 01:02:50,870 --> 01:02:53,210 The title of the page is Tasks. 1234 01:02:53,210 --> 01:02:56,930 Ultimately I just copied over the content of index.html 1235 01:02:56,930 --> 01:03:01,660 and pasted it into this new HTML page add.html. 1236 01:03:01,660 --> 01:03:03,560 And any time you find yourself copy pasting, 1237 01:03:03,560 --> 01:03:05,352 this should be another area where you start 1238 01:03:05,352 --> 01:03:07,610 to think there is probably a better way to do this. 1239 01:03:07,610 --> 01:03:10,430 And with just pure HTML, there kind of wasn't. 1240 01:03:10,430 --> 01:03:14,540 If we wanted in multiple different HTML pages that displayed similar content, 1241 01:03:14,540 --> 01:03:18,560 we needed the same HTML on all those different pages. 1242 01:03:18,560 --> 01:03:21,020 But now in the world of Django, we have the ability 1243 01:03:21,020 --> 01:03:23,970 to use something called template inheritance. 1244 01:03:23,970 --> 01:03:28,010 What I'm now going to do is define an HTML file called the layout, just 1245 01:03:28,010 --> 01:03:33,080 some file that the other files add.html and index.html 1246 01:03:33,080 --> 01:03:34,820 are going to inherit from. 1247 01:03:34,820 --> 01:03:38,750 They're going to inherit from my layout all of the structure of the page that's 1248 01:03:38,750 --> 01:03:40,880 the same on both of the pages. 1249 01:03:40,880 --> 01:03:44,330 And all I then need to write is what differs between the pages. 1250 01:03:44,330 --> 01:03:46,280 And as I mentioned before, the only thing 1251 01:03:46,280 --> 01:03:50,811 that differs between add.html and index.html 1252 01:03:50,811 --> 01:03:55,670 is the content of the body of the page over here. 1253 01:03:55,670 --> 01:03:57,070 So what can I now do? 1254 01:03:57,070 --> 01:04:01,630 Well, I will create a new file inside of my template/tasks directory called 1255 01:04:01,630 --> 01:04:04,540 layout.html. 1256 01:04:04,540 --> 01:04:08,860 And layout is going to have the basic layout that 1257 01:04:08,860 --> 01:04:11,450 is common to both of these pages. 1258 01:04:11,450 --> 01:04:13,690 I have a title whose title is Tasks. 1259 01:04:13,690 --> 01:04:15,047 I have the body of the page. 1260 01:04:15,047 --> 01:04:17,380 And maybe there is more in common with both of the pages 1261 01:04:17,380 --> 01:04:18,700 as well that I could add. 1262 01:04:18,700 --> 01:04:21,430 But right here, this, in between the body tags, 1263 01:04:21,430 --> 01:04:25,180 this is the body of the page that is going to change 1264 01:04:25,180 --> 01:04:28,230 between each of the different pages. 1265 01:04:28,230 --> 01:04:31,120 So to denote this inside of it in layout in Django, 1266 01:04:31,120 --> 01:04:33,880 I'll again use the curly brace and percent signs. 1267 01:04:33,880 --> 01:04:35,740 I'll call this a block. 1268 01:04:35,740 --> 01:04:37,282 And then I'll give this block a name. 1269 01:04:37,282 --> 01:04:39,573 I'll call it body, just because it's the body the page, 1270 01:04:39,573 --> 01:04:41,140 but I could give it any name. 1271 01:04:41,140 --> 01:04:46,060 And then I'll add an end the block at the bottom here. 1272 01:04:46,060 --> 01:04:48,820 And so what I've now said inside of this layout file 1273 01:04:48,820 --> 01:04:53,170 is that this layout file has a body inside of this structure of the page. 1274 01:04:53,170 --> 01:04:56,257 And inside the body is this block called body. 1275 01:04:56,257 --> 01:04:58,090 And what I'm saying here is this block might 1276 01:04:58,090 --> 01:05:03,850 change depending on which file we're using, add.html or index.html. 1277 01:05:03,850 --> 01:05:07,270 The rest of the structure won't change, but the content of this block, 1278 01:05:07,270 --> 01:05:11,080 this block called body, might change. 1279 01:05:11,080 --> 01:05:16,540 And so now what I can do inside of index.html and add.html is, 1280 01:05:16,540 --> 01:05:20,170 instead of including all of this logic, I can get rid of everything 1281 01:05:20,170 --> 01:05:22,840 other than the key part of the page that's 1282 01:05:22,840 --> 01:05:25,330 going to be different about index.html. 1283 01:05:25,330 --> 01:05:30,430 The only thing different about index.html is this unordered list. 1284 01:05:30,430 --> 01:05:34,750 And what I will now included the top of index.html is I'll say that this HTML 1285 01:05:34,750 --> 01:05:40,570 page extends tasks/layout.html. 1286 01:05:40,570 --> 01:05:42,880 So this is now this idea of template inheritance. 1287 01:05:42,880 --> 01:05:46,210 I'm inheriting from the layout.html template, 1288 01:05:46,210 --> 01:05:52,840 basically saying use the layout.html template, except inside of block body, 1289 01:05:52,840 --> 01:05:57,170 I would like to include all of this content. 1290 01:05:57,170 --> 01:06:02,840 And maybe I'll give it a h1 that just says Tasks just as a title as well. 1291 01:06:02,840 --> 01:06:05,740 And so now what index.html is saying is, rather than need 1292 01:06:05,740 --> 01:06:09,130 to include all of that HTML, all I need to say 1293 01:06:09,130 --> 01:06:13,270 is this HTML file is based on the layout.html file, 1294 01:06:13,270 --> 01:06:16,390 but the difference is that inside the body of the page, 1295 01:06:16,390 --> 01:06:18,780 it's going to be this particular content here. 1296 01:06:18,780 --> 01:06:21,940 And for add.html, I can do exactly the same thing. 1297 01:06:21,940 --> 01:06:26,740 I can just add this line, extends tasks/layout.html to the top 1298 01:06:26,740 --> 01:06:28,740 of add.html. 1299 01:06:28,740 --> 01:06:32,230 And then I can get rid of all of this boilerplate code, 1300 01:06:32,230 --> 01:06:36,520 so to speak, and just include the part of the page that 1301 01:06:36,520 --> 01:06:41,530 differs inside the body of the page. 1302 01:06:41,530 --> 01:06:44,780 And so it seems like we've done a fair bit more work for just these two pages. 1303 01:06:44,780 --> 01:06:46,488 But if you imagine more complex websites, 1304 01:06:46,488 --> 01:06:48,820 and they have dozens or hundreds of different pages, 1305 01:06:48,820 --> 01:06:52,540 the ability to factor out the HTML the pages have in common 1306 01:06:52,540 --> 01:06:55,270 can definitely be very helpful just for good design, 1307 01:06:55,270 --> 01:06:57,620 to be able to make sure we're not repeating ourselves. 1308 01:06:57,620 --> 01:06:59,560 And if we ever need to change the structure, 1309 01:06:59,560 --> 01:07:02,410 rather than change it in dozens or hundreds of different places, 1310 01:07:02,410 --> 01:07:05,382 we just change it in one place inside of the layout file. 1311 01:07:05,382 --> 01:07:07,090 And the result of that is that it's going 1312 01:07:07,090 --> 01:07:12,290 to change in each of the pages that inherit from that page as well. 1313 01:07:12,290 --> 01:07:17,320 And we can test this by just going back to /task/add, which looks fine. 1314 01:07:17,320 --> 01:07:19,420 And back to tasks looks fine too. 1315 01:07:19,420 --> 01:07:24,970 Both of these pages now are inheriting from that basic layout. 1316 01:07:24,970 --> 01:07:28,600 Now, it's a little annoying that anytime I want to switch between this page 1317 01:07:28,600 --> 01:07:31,840 and the add page, I have to go to the URL and know that I need to go 1318 01:07:31,840 --> 01:07:36,020 to /tasks/add in order to get back and forth between them. 1319 01:07:36,020 --> 01:07:39,550 So I might like to add a link that takes me from one page to the other and vice 1320 01:07:39,550 --> 01:07:40,480 versa. 1321 01:07:40,480 --> 01:07:42,400 And I can do that. 1322 01:07:42,400 --> 01:07:45,040 if I go into index.html, you might imagine 1323 01:07:45,040 --> 01:07:48,670 that I could just add a link here, a href to create a link. 1324 01:07:48,670 --> 01:07:51,670 Let's go to /tasks/add. 1325 01:07:51,670 --> 01:07:56,380 And, like, Add a New Task would be the name of that link. 1326 01:07:56,380 --> 01:07:59,500 Except the problem is or the reason why this isn't necessarily good 1327 01:07:59,500 --> 01:08:02,050 design is that Django is designed to make it such 1328 01:08:02,050 --> 01:08:05,350 that it's very easy to change the structure of the pages 1329 01:08:05,350 --> 01:08:08,290 in terms of how the URLs all relate to each other. 1330 01:08:08,290 --> 01:08:14,230 And I have here hardcoded that, when you click this link, we go to /tasks/add. 1331 01:08:14,230 --> 01:08:16,029 And if ever I wanted to change that URL-- 1332 01:08:16,029 --> 01:08:20,590 maybe instead of /tasks/add, I wanted to /new instead of /add-- 1333 01:08:20,590 --> 01:08:22,850 well, then I need to change it in two places. 1334 01:08:22,850 --> 01:08:25,720 I'd need to go back to the urls.py file to change 1335 01:08:25,720 --> 01:08:28,899 the actual URL to say, instead of add, it should be called new. 1336 01:08:28,899 --> 01:08:32,170 But then I'd need to find every place where I use that URL 1337 01:08:32,170 --> 01:08:34,760 and I'd need to change it there as well. 1338 01:08:34,760 --> 01:08:37,390 So in order to deal with this, Django has an additional feature 1339 01:08:37,390 --> 01:08:41,950 that basically lets Django figure out what the URL should be instead. 1340 01:08:41,950 --> 01:08:45,859 And we do that by using the name that we gave to each of those routes. 1341 01:08:45,859 --> 01:08:48,760 And so this is where that name becomes relevant, that I can hear just 1342 01:08:48,760 --> 01:08:57,850 say, link to a particular URL, link to the URL called add. 1343 01:08:57,850 --> 01:09:00,720 So I just said, link to URL called add. 1344 01:09:00,720 --> 01:09:05,819 And how Django figures this out is based on the contents of my urls.py, 1345 01:09:05,819 --> 01:09:09,479 that inside my urls.py file, I defined a number of different paths. 1346 01:09:09,479 --> 01:09:11,350 And I gave each of those paths a name. 1347 01:09:11,350 --> 01:09:12,810 This one was called index. 1348 01:09:12,810 --> 01:09:14,609 This one was called add. 1349 01:09:14,609 --> 01:09:19,040 And so when I did that, I was able to say, if you link to a URL called add, 1350 01:09:19,040 --> 01:09:23,450 Django will find a URL whose name is add and link me directly to that route. 1351 01:09:23,450 --> 01:09:25,950 And so if ever I were to change the route to something else, 1352 01:09:25,950 --> 01:09:28,356 Django would just figure out what the new URL should be. 1353 01:09:28,356 --> 01:09:29,939 And I wouldn't have to worry about it. 1354 01:09:29,939 --> 01:09:33,200 Django would fix the problems for me. 1355 01:09:33,200 --> 01:09:36,090 And so now if I go back to the tasks site, 1356 01:09:36,090 --> 01:09:38,189 I can click the Add a New Task button. 1357 01:09:38,189 --> 01:09:40,853 And that will take me to the add task. 1358 01:09:40,853 --> 01:09:43,020 And maybe now I'd like to add a link that goes back. 1359 01:09:43,020 --> 01:09:49,020 So I can go to add.html and maybe add a link down at the bottom, a href equals. 1360 01:09:49,020 --> 01:09:51,029 And what URL would I like to link to? 1361 01:09:51,029 --> 01:09:53,612 Well, my default page was just called index. 1362 01:09:53,612 --> 01:09:55,320 So I'll go ahead include the word index-- 1363 01:09:55,320 --> 01:09:58,530 I'd like to link to the URL index-- 1364 01:09:58,530 --> 01:10:04,050 and then view tasks, maybe, as the name of the link. 1365 01:10:04,050 --> 01:10:06,540 So now if I go to just the default tasks page, 1366 01:10:06,540 --> 01:10:08,277 I see a link to go Add a New Task. 1367 01:10:08,277 --> 01:10:10,110 And now I see a link that's going to take me 1368 01:10:10,110 --> 01:10:12,090 back to the ability to view tasks. 1369 01:10:12,090 --> 01:10:15,138 And when I click on that link, I see "NO." 1370 01:10:15,138 --> 01:10:16,930 And now, that's probably not what I wanted. 1371 01:10:16,930 --> 01:10:20,220 I wanted to go back to the index page for my tasks application, 1372 01:10:20,220 --> 01:10:24,365 but it seems that when I clicked on View Tasks, I'm taken to "NO." 1373 01:10:24,365 --> 01:10:25,500 And what's going on here? 1374 01:10:25,500 --> 01:10:30,060 Well, if you look at the URL, the URL is /newyear. 1375 01:10:30,060 --> 01:10:31,710 Somehow I was on the tasks app. 1376 01:10:31,710 --> 01:10:33,130 I clicked a link. 1377 01:10:33,130 --> 01:10:34,890 And I'm back at the newyear app. 1378 01:10:34,890 --> 01:10:36,400 How did that happen? 1379 01:10:36,400 --> 01:10:39,600 Well, it turns out this is an example of a namespace collision, where I have 1380 01:10:39,600 --> 01:10:42,790 two things that all have the same name. 1381 01:10:42,790 --> 01:10:44,760 And in this case, what's happening is that I 1382 01:10:44,760 --> 01:10:49,740 have a urls.py file for my tasks application 1383 01:10:49,740 --> 01:10:53,640 where I have a route called add and a route called index. 1384 01:10:53,640 --> 01:10:57,360 But it just so happens that inside of the newyear folder, 1385 01:10:57,360 --> 01:11:01,920 if I go up to newyear and I look at newyear's urls.py file, 1386 01:11:01,920 --> 01:11:06,780 the newyear's urls.py file also has a path whose name is index. 1387 01:11:06,780 --> 01:11:09,210 And so what I said was, create a link. 1388 01:11:09,210 --> 01:11:11,760 And I would like that link to link to the thing that 1389 01:11:11,760 --> 01:11:14,508 has the URL with a name of index. 1390 01:11:14,508 --> 01:11:17,550 And it turns out there were multiple things that all had a name of index. 1391 01:11:17,550 --> 01:11:19,300 And so Django didn't know which to choose. 1392 01:11:19,300 --> 01:11:21,180 And it just chose the newyear one. 1393 01:11:21,180 --> 01:11:23,280 And you might imagine that linking between apps 1394 01:11:23,280 --> 01:11:25,155 is something you might reasonably want to do. 1395 01:11:25,155 --> 01:11:27,578 You want to, from the Amazon shopping page, for example, 1396 01:11:27,578 --> 01:11:29,870 be able to click a link that takes you to Amazon Video. 1397 01:11:29,870 --> 01:11:32,037 Or you want, from Google search, to be able to click 1398 01:11:32,037 --> 01:11:33,630 a button that gets you to Google Maps. 1399 01:11:33,630 --> 01:11:36,300 But in this case, this isn't quite what I wanted. 1400 01:11:36,300 --> 01:11:39,540 There is a namespace collision where two things have the same name that I 1401 01:11:39,540 --> 01:11:42,150 would now like to be able to fix. 1402 01:11:42,150 --> 01:11:48,210 And the easy way to fix this is inside of urls.py for my tasks application, 1403 01:11:48,210 --> 01:11:54,030 let me just give each of these URLs an app_name called tasks. 1404 01:11:54,030 --> 01:11:57,270 This just helps uniquely identify all of the URLs, 1405 01:11:57,270 --> 01:12:02,160 because now inside of add.html, rather than just link to a URL whose name is 1406 01:12:02,160 --> 01:12:08,580 index, I'm going to instead link to tasks:index, to mean, from tasks, 1407 01:12:08,580 --> 01:12:12,480 the tasks app, get the index URL. 1408 01:12:12,480 --> 01:12:18,030 And likewise, inside of index.html, I'll link to tasks:add to get at that 1409 01:12:18,030 --> 01:12:22,890 particular route from this particular application name. 1410 01:12:22,890 --> 01:12:27,150 So now if I go back to the site, go back to my tasks page, 1411 01:12:27,150 --> 01:12:28,470 now the links work as expected. 1412 01:12:28,470 --> 01:12:30,030 I can get to the new tasks. 1413 01:12:30,030 --> 01:12:35,250 And I can get back to the list of all of my tasks as well. 1414 01:12:35,250 --> 01:12:36,000 So this now works. 1415 01:12:36,000 --> 01:12:39,150 I now have these two pages, one that displays my list, one that 1416 01:12:39,150 --> 01:12:41,400 displays my ability to add a new task. 1417 01:12:41,400 --> 01:12:44,910 But the form to add a new task doesn't really do anything right now. 1418 01:12:44,910 --> 01:12:45,780 I type in a task. 1419 01:12:45,780 --> 01:12:48,570 I type in, like, foo, if I want to add a task called foo. 1420 01:12:48,570 --> 01:12:49,527 And I press Submit. 1421 01:12:49,527 --> 01:12:50,610 And like, nothing happens. 1422 01:12:50,610 --> 01:12:53,610 Nothing changes meaningfully on this site. 1423 01:12:53,610 --> 01:12:55,930 So I'd like for this form to do something. 1424 01:12:55,930 --> 01:12:59,730 And we've seen that we can add an action to a form to be able to take that form 1425 01:12:59,730 --> 01:13:01,540 and submit it somewhere. 1426 01:13:01,540 --> 01:13:05,520 And that's what I'd like to do when I add a new task. 1427 01:13:05,520 --> 01:13:07,890 I'm going to add an action to this form. 1428 01:13:07,890 --> 01:13:11,340 And when I submit the form, what URL would l like to submit it to? 1429 01:13:11,340 --> 01:13:18,210 Well, I'll go ahead and submit it back to the URL for tasks:add. 1430 01:13:18,210 --> 01:13:22,360 I'll send it back to that add URL when I submit the form. 1431 01:13:22,360 --> 01:13:25,050 And I'm going to give this form a specific request method. 1432 01:13:25,050 --> 01:13:27,420 Its method is going to be post. 1433 01:13:27,420 --> 01:13:29,700 And so we've already seen a request method of get. 1434 01:13:29,700 --> 01:13:33,360 Anytime you type in a URL or click on a link to go to another page, 1435 01:13:33,360 --> 01:13:36,360 the request method implicitly associated with that request 1436 01:13:36,360 --> 01:13:40,770 is called get, which just means I would like to get a particular page. 1437 01:13:40,770 --> 01:13:43,650 Anytime you're submitting data that has the potential 1438 01:13:43,650 --> 01:13:46,500 to change some state inside the application 1439 01:13:46,500 --> 01:13:48,930 like changing the state of the list of tasks that 1440 01:13:48,930 --> 01:13:52,560 is stored inside the application, then we'll generally use a different request 1441 01:13:52,560 --> 01:13:54,822 method called post. 1442 01:13:54,822 --> 01:13:57,380 Post is generally used for submitting form data. 1443 01:13:57,380 --> 01:13:59,630 It doesn't include parameters inside the URL 1444 01:13:59,630 --> 01:14:02,690 the way a get request does, as we saw with Google, for example. 1445 01:14:02,690 --> 01:14:04,730 But this post ability is going to give us 1446 01:14:04,730 --> 01:14:08,570 the ability now to send data via a different request method 1447 01:14:08,570 --> 01:14:11,140 to my add route. 1448 01:14:11,140 --> 01:14:13,420 And so let's now give this a try. 1449 01:14:13,420 --> 01:14:15,980 Now I'm going to go to task/add. 1450 01:14:15,980 --> 01:14:17,720 I see the ability to add a task. 1451 01:14:17,720 --> 01:14:22,970 And maybe I'll add a task like check email or something and press Submit. 1452 01:14:22,970 --> 01:14:25,420 And all right, I get an error, forbidden. 1453 01:14:25,420 --> 01:14:28,970 This 403 in parentheses means that is the response code that came back. 1454 01:14:28,970 --> 01:14:31,530 This is an error that Django has generated for me. 1455 01:14:31,530 --> 01:14:33,980 So this 403, as we saw before, means forbidden. 1456 01:14:33,980 --> 01:14:36,540 I don't have permission to do this for some reason. 1457 01:14:36,540 --> 01:14:38,840 Now, why don't I have permission to submit this form? 1458 01:14:38,840 --> 01:14:42,410 It says, CSRF verification failed. 1459 01:14:42,410 --> 01:14:46,220 So CSRF stands for Cross-Site Request Forgery. 1460 01:14:46,220 --> 01:14:49,190 And what that means, it is a security vulnerability 1461 01:14:49,190 --> 01:14:52,520 that is inherent in some forms if they're not designed in a secure way, 1462 01:14:52,520 --> 01:14:57,050 meaning that someone could forge a request to a particular website using 1463 01:14:57,050 --> 01:15:00,140 some form on their own separate website, for example. 1464 01:15:00,140 --> 01:15:02,540 You might imagine that someone on a different website 1465 01:15:02,540 --> 01:15:04,790 might trick the user into submitting a form that 1466 01:15:04,790 --> 01:15:07,790 submits its data to our add task function that 1467 01:15:07,790 --> 01:15:10,250 adds a new task to their task list. 1468 01:15:10,250 --> 01:15:12,127 And maybe that's not a big deal for tasks, 1469 01:15:12,127 --> 01:15:14,960 but you might imagine in more secure context, more sensitive context 1470 01:15:14,960 --> 01:15:16,910 like a bank, for example, they might have 1471 01:15:16,910 --> 01:15:20,780 a form on their website for transferring money from one user to another. 1472 01:15:20,780 --> 01:15:22,910 And if they're vulnerable to this sort of attack, 1473 01:15:22,910 --> 01:15:26,420 cross-site request forgery, someone else on a different website 1474 01:15:26,420 --> 01:15:30,550 could trick the user into submitting a form where it submits form data. 1475 01:15:30,550 --> 01:15:32,420 And it goes to the bank's website to say, 1476 01:15:32,420 --> 01:15:35,780 I would like to transfer money from this one user to another user. 1477 01:15:35,780 --> 01:15:38,390 So we would like to be able to design forms that are not 1478 01:15:38,390 --> 01:15:41,690 vulnerable to that particular security vulnerability, that 1479 01:15:41,690 --> 01:15:46,460 don't allow for requests to be forged by another website. 1480 01:15:46,460 --> 01:15:48,090 So how can we go about doing this? 1481 01:15:48,090 --> 01:15:49,940 Well, one strategy that can be used in order 1482 01:15:49,940 --> 01:15:53,840 to deal with these sorts of attacks is to add into our form 1483 01:15:53,840 --> 01:15:57,930 a hidden Cross-Site Request Forgery token, or CSRF token, 1484 01:15:57,930 --> 01:16:01,220 which would just be some unique token that's generated for every session. 1485 01:16:01,220 --> 01:16:04,070 So every time a different user visits this particular form, 1486 01:16:04,070 --> 01:16:06,350 they see a different CSRF token. 1487 01:16:06,350 --> 01:16:08,450 And then when the user submits the form, they're 1488 01:16:08,450 --> 01:16:10,700 submitting that token with the form. 1489 01:16:10,700 --> 01:16:13,120 And our web application is going to check to make sure 1490 01:16:13,120 --> 01:16:14,700 that that token is indeed valid. 1491 01:16:14,700 --> 01:16:18,210 And if it is valid, then they'll allow the form submission to go through. 1492 01:16:18,210 --> 01:16:20,240 But this means that an adversary wouldn't 1493 01:16:20,240 --> 01:16:22,370 be able to fake a request to our website, 1494 01:16:22,370 --> 01:16:25,250 because that adversary doesn't know specifically 1495 01:16:25,250 --> 01:16:27,440 what the generated token is so they would 1496 01:16:27,440 --> 01:16:31,300 fail the check for CSRF validation. 1497 01:16:31,300 --> 01:16:35,720 And it just so happens that Django has CSRF validation turned on by default. 1498 01:16:35,720 --> 01:16:39,770 And it's done so via specific add-on known as Django Middleware. 1499 01:16:39,770 --> 01:16:42,110 Middleware refers to the ability in Django 1500 01:16:42,110 --> 01:16:46,460 to be able to sort of intervene in the request response processing of a Django 1501 01:16:46,460 --> 01:16:47,450 request. 1502 01:16:47,450 --> 01:16:50,600 And if I look at the settings.py file, if you're curious, 1503 01:16:50,600 --> 01:16:56,210 for this particular web application, inside of settings,py, 1504 01:16:56,210 --> 01:16:59,030 if we scroll down, we see there's a whole bunch of middleware 1505 01:16:59,030 --> 01:17:02,930 that's installed by default inside of a Django application in terms of making 1506 01:17:02,930 --> 01:17:05,930 sure that we have various different features hooked into this request 1507 01:17:05,930 --> 01:17:07,190 response processing. 1508 01:17:07,190 --> 01:17:12,470 And one of those is the CSRF view middleware, this feature of Django 1509 01:17:12,470 --> 01:17:14,750 that allows it to make sure that our requests, 1510 01:17:14,750 --> 01:17:17,990 whenever we're submitting data via post, something that has the potential 1511 01:17:17,990 --> 01:17:21,470 to change the state of the application in some way, 1512 01:17:21,470 --> 01:17:23,910 that we need to have CSRF validation. 1513 01:17:23,910 --> 01:17:26,330 We need to add some sort of token to our form 1514 01:17:26,330 --> 01:17:31,010 to make sure that Django is able to authenticate the validity of this form 1515 01:17:31,010 --> 01:17:36,060 to make sure they know the form actually came from the web application itself. 1516 01:17:36,060 --> 01:17:39,140 And it's quite easy to be able to add this token into our HTML page. 1517 01:17:39,140 --> 01:17:40,240 Django has it built in. 1518 01:17:40,240 --> 01:17:43,160 Inside the curly brace and percent sign, we can just say, 1519 01:17:43,160 --> 01:17:47,030 I would like to add into this page the CSRF 1520 01:17:47,030 --> 01:17:52,670 token, for example, to go ahead and fill it in the CSRF token right there. 1521 01:17:52,670 --> 01:17:57,170 If I now go back to the page and refresh the page, now I see add task. 1522 01:17:57,170 --> 01:18:00,650 But if we're curious, I can actually go and view the page source. 1523 01:18:00,650 --> 01:18:02,450 I can look inside this page. 1524 01:18:02,450 --> 01:18:04,370 And here now is the form. 1525 01:18:04,370 --> 01:18:06,260 And this is the form same as we saw before, 1526 01:18:06,260 --> 01:18:09,200 but you'll notice that Django has inserted this additional input 1527 01:18:09,200 --> 01:18:11,510 field, this input whose type is hidden, meaning 1528 01:18:11,510 --> 01:18:15,680 we won't be able to see it normally, whose name is CSRF middleware token. 1529 01:18:15,680 --> 01:18:18,620 And here is its value, some long string of characters 1530 01:18:18,620 --> 01:18:22,230 that Django has generated for me, such that when I submit this form, 1531 01:18:22,230 --> 01:18:24,600 it's going to check to make sure this token is valid. 1532 01:18:24,600 --> 01:18:28,870 And if it doesn't find this token, it is not going to accept my form submission. 1533 01:18:28,870 --> 01:18:30,960 And if someone else goes to this website, 1534 01:18:30,960 --> 01:18:34,560 they are going to see a different token presented to them as well. 1535 01:18:34,560 --> 01:18:38,960 And that helps to make sure that nobody can forge these sorts of requests. 1536 01:18:38,960 --> 01:18:42,620 So now if I type in a task that I like to add , something like check email, 1537 01:18:42,620 --> 01:18:45,793 and press Submit, now the form does submit without errors. 1538 01:18:45,793 --> 01:18:47,210 Of course, it doesn't do anything. 1539 01:18:47,210 --> 01:18:49,700 If I go back to my task list, it's still empty. 1540 01:18:49,700 --> 01:18:52,810 But at least I've now been able to submit that form. 1541 01:18:52,810 --> 01:18:56,510 It's worth noting that inside of add.html, 1542 01:18:56,510 --> 01:18:58,580 we created this form sort of from scratch. 1543 01:18:58,580 --> 01:19:02,087 I created an input field whose type is text and whose name is task. 1544 01:19:02,087 --> 01:19:03,920 But creating forms is something that happens 1545 01:19:03,920 --> 01:19:06,468 so often in the world of web programming, oftentimes 1546 01:19:06,468 --> 01:19:09,260 with many different fields that you might want to change over time, 1547 01:19:09,260 --> 01:19:13,130 but Django has added a number of ways to make it easier to create forms, 1548 01:19:13,130 --> 01:19:15,560 to validate the data inside of those forms 1549 01:19:15,560 --> 01:19:18,590 just to make our lives a little bit easier when it comes to dealing 1550 01:19:18,590 --> 01:19:20,240 with and interacting with forms. 1551 01:19:20,240 --> 01:19:24,030 And so now we'll explore an alternative way of doing the same thing. 1552 01:19:24,030 --> 01:19:25,410 What we did here just works. 1553 01:19:25,410 --> 01:19:29,750 We can create a form just using raw HTML, as we've seen before. 1554 01:19:29,750 --> 01:19:33,110 But Django also has the ability to create forms for us. 1555 01:19:33,110 --> 01:19:36,230 So in order to do this, I'll go into views.py and at the top, 1556 01:19:36,230 --> 01:19:40,160 add from django import forms. 1557 01:19:40,160 --> 01:19:43,280 And now I'm going to create a new class to represent this form. 1558 01:19:43,280 --> 01:19:46,570 I'll create a Python class that I'll just call NewTaskForm, 1559 01:19:46,570 --> 01:19:48,830 since I'll use it to create a new task. 1560 01:19:48,830 --> 01:19:51,670 It will inherit from forms.form. 1561 01:19:51,670 --> 01:19:53,960 And now inside of this class, I need to define 1562 01:19:53,960 --> 01:19:57,710 all of the fields I would like for this form to have, all of the inputs 1563 01:19:57,710 --> 01:19:59,470 that I would like the user to provide. 1564 01:19:59,470 --> 01:20:01,970 And so I want them to provide the name of a task which 1565 01:20:01,970 --> 01:20:04,430 will be a character field, or a CharField, 1566 01:20:04,430 --> 01:20:06,950 meaning I want the user to type in characters. 1567 01:20:06,950 --> 01:20:11,270 And I can give this a label, call it New Task, for example. 1568 01:20:11,270 --> 01:20:16,520 And now what I can do is, when I render add.html, I can add some context 1569 01:20:16,520 --> 01:20:20,180 and say, give this template access to a variable called form which 1570 01:20:20,180 --> 01:20:22,350 will just be a new task form. 1571 01:20:22,350 --> 01:20:26,930 So I'm going to create a new task form, pass it into this add.html template. 1572 01:20:26,930 --> 01:20:31,430 And now inside of add.html, instead of writing the input whose type is text, 1573 01:20:31,430 --> 01:20:34,160 name is task, and having to do that on my own, 1574 01:20:34,160 --> 01:20:38,440 I can just use double curly braces and say, plug in the form here. 1575 01:20:38,440 --> 01:20:40,190 And that will automatically take care of-- 1576 01:20:40,190 --> 01:20:45,540 Django will generate the necessary HTML to make that form work. 1577 01:20:45,540 --> 01:20:49,038 So if I refresh this page, now I see that here is the form 1578 01:20:49,038 --> 01:20:50,330 that Django has created for me. 1579 01:20:50,330 --> 01:20:51,310 It's created an input field. 1580 01:20:51,310 --> 01:20:54,352 It's given it a label of New Task, so I know that it's where in new tasks 1581 01:20:54,352 --> 01:20:54,950 should go. 1582 01:20:54,950 --> 01:20:58,220 But now rather than needing to edit the HTML anytime 1583 01:20:58,220 --> 01:21:01,730 I want to change the form data that's involved inside of this application, 1584 01:21:01,730 --> 01:21:03,830 I can just change this new task form. 1585 01:21:03,830 --> 01:21:06,950 If maybe I want to eventually make upgrades to my application, 1586 01:21:06,950 --> 01:21:10,700 where in addition to specifying a text field where I can type in the new task, 1587 01:21:10,700 --> 01:21:14,660 maybe I also want to be able to specify a number indicating the priority 1588 01:21:14,660 --> 01:21:18,140 that task should have, I could additionally give this form access 1589 01:21:18,140 --> 01:21:24,220 to a priority variable, which is an integer field whose label is priority. 1590 01:21:24,220 --> 01:21:25,910 And I can even add constraints on this. 1591 01:21:25,910 --> 01:21:28,370 To be able to ensure that it's valid data, 1592 01:21:28,370 --> 01:21:33,380 I can give it a min value of maybe 1 and a max value of 10, for example, 1593 01:21:33,380 --> 01:21:34,880 in order to add all of that. 1594 01:21:34,880 --> 01:21:37,130 And now without touching anything in my HTML, 1595 01:21:37,130 --> 01:21:40,950 I just changed the form itself inside of my Python code, 1596 01:21:40,950 --> 01:21:43,460 now if I refresh the page, I see an additional field. 1597 01:21:43,460 --> 01:21:46,160 I see an opportunity for me to type in a new task. 1598 01:21:46,160 --> 01:21:49,250 And I see an opportunity for me to specify some priority. 1599 01:21:49,250 --> 01:21:51,830 And of course, you could add CSS in order to style this up 1600 01:21:51,830 --> 01:21:53,100 a little bit nicer. 1601 01:21:53,100 --> 01:21:57,760 But now Django will automatically do client-side validation, 1602 01:21:57,760 --> 01:22:02,000 where if I type in a new task like check email but I don't specify a priority, 1603 01:22:02,000 --> 01:22:04,117 and submit it, it tells me to fill it out. 1604 01:22:04,117 --> 01:22:06,200 If I type in a number that's in an invalid range-- 1605 01:22:06,200 --> 01:22:08,210 I only wanted numbers from 0 to 10-- 1606 01:22:08,210 --> 01:22:09,440 it fills it out. 1607 01:22:09,440 --> 01:22:12,560 And this is all what we would call, again, client-side validation. 1608 01:22:12,560 --> 01:22:14,600 The server isn't getting any of this data. 1609 01:22:14,600 --> 01:22:19,048 It's just the web page has been encoded to know what the valid values are. 1610 01:22:19,048 --> 01:22:20,840 And it's going to constrain me to make sure 1611 01:22:20,840 --> 01:22:23,780 that I'm typing in something that matches those values. 1612 01:22:23,780 --> 01:22:26,780 But in general, when we're doing form validation, when we're making sure 1613 01:22:26,780 --> 01:22:29,450 that forms are valid data, we want to somehow 1614 01:22:29,450 --> 01:22:35,030 make sure to include not only client-side validation, but also 1615 01:22:35,030 --> 01:22:36,350 server-side validation. 1616 01:22:36,350 --> 01:22:40,520 We also want to check on the server to make sure that inputs are valid 1617 01:22:40,520 --> 01:22:42,650 as well, because there are many reasons why 1618 01:22:42,650 --> 01:22:44,150 we might want to be able to do this. 1619 01:22:44,150 --> 01:22:47,420 One is that it's very easy to disable this sort of client-side validation, 1620 01:22:47,420 --> 01:22:50,880 or just submit a request without doing any of the client-side validation. 1621 01:22:50,880 --> 01:22:53,710 And maybe if the user is looking at an old version of the page, 1622 01:22:53,710 --> 01:22:56,340 the validation we're doing on the server is more up to date 1623 01:22:56,340 --> 01:22:58,600 than this client-side validation as well. 1624 01:22:58,600 --> 01:23:02,050 And Django's forms will make it very easy for us to do both of these things, 1625 01:23:02,050 --> 01:23:06,873 both the client-side validation and the server-side validation as well. 1626 01:23:06,873 --> 01:23:07,790 So how does this work? 1627 01:23:07,790 --> 01:23:09,080 How do we do that? 1628 01:23:09,080 --> 01:23:12,310 Well, inside of the add function, this add 1629 01:23:12,310 --> 01:23:14,690 function now gets called in two different ways 1630 01:23:14,690 --> 01:23:16,900 depending upon the request method. 1631 01:23:16,900 --> 01:23:21,310 If I try to get the add page by just clicking on the link for Add a New Task 1632 01:23:21,310 --> 01:23:26,180 or going to the URL of /add, then I want to just render a new blank form. 1633 01:23:26,180 --> 01:23:31,060 But if I post data to this page by using the post request method instead of get, 1634 01:23:31,060 --> 01:23:32,970 that means I'm submitting the form. 1635 01:23:32,970 --> 01:23:37,760 And I now want to submit a new task to be added to my list of tasks. 1636 01:23:37,760 --> 01:23:39,640 So I'd like to add a check for that. 1637 01:23:39,640 --> 01:23:45,850 I'm going add a condition here that says, if request.method equals POST, 1638 01:23:45,850 --> 01:23:50,482 well, then here what I'd like to do is process the result of that request. 1639 01:23:50,482 --> 01:23:53,440 And the way you do that and a Django form is by creating a new variable 1640 01:23:53,440 --> 01:23:56,300 called form which will be a NewTaskForm. 1641 01:23:56,300 --> 01:23:59,830 And if I just use NewTaskForm with two parentheses like I did before, 1642 01:23:59,830 --> 01:24:01,840 that creates a blank form. 1643 01:24:01,840 --> 01:24:05,320 But you can also populate that form with some data. 1644 01:24:05,320 --> 01:24:09,910 And if I populate it with request.post, what that is going to do 1645 01:24:09,910 --> 01:24:13,600 is request.post contains all of the data that the user 1646 01:24:13,600 --> 01:24:16,316 submitted when they submitted the form. 1647 01:24:16,316 --> 01:24:18,700 And so what I'm doing now is I'm creating a form 1648 01:24:18,700 --> 01:24:22,030 variable by taking all of that data and filling it 1649 01:24:22,030 --> 01:24:26,090 into this new task form, which will contain now all of the data 1650 01:24:26,090 --> 01:24:27,410 the user submitted. 1651 01:24:27,410 --> 01:24:29,410 And you could imagine checking this manually, 1652 01:24:29,410 --> 01:24:36,250 but I can just call if form.is_valid, and inside this if statement, 1653 01:24:36,250 --> 01:24:40,970 use some logic using the cleaned data as a result of using this form. 1654 01:24:40,970 --> 01:24:44,740 And so inside of a variable called form.cleaned_data, 1655 01:24:44,740 --> 01:24:48,530 that will give me access to all of the data the user submitted. 1656 01:24:48,530 --> 01:24:51,280 And so if I want to get what task they submitted, 1657 01:24:51,280 --> 01:24:55,540 because I had a variable here called task inside of NewTaskForm, well, 1658 01:24:55,540 --> 01:25:00,040 I'll just go to form.cleaned_data and then task. 1659 01:25:00,040 --> 01:25:03,370 And I'll go ahead and save this inside of a variable called task. 1660 01:25:03,370 --> 01:25:07,530 And now what I might like to do is add this task to my list of tasks, 1661 01:25:07,530 --> 01:25:11,760 go tasks.append this new task. 1662 01:25:11,760 --> 01:25:13,510 So that's what we do if the form is valid. 1663 01:25:13,510 --> 01:25:17,570 If the form is valid, we take the data from the form, get the task, 1664 01:25:17,570 --> 01:25:22,180 save it inside this variable called tasks, and add it to my growing list. 1665 01:25:22,180 --> 01:25:27,230 But else if the form is not valid, what should we do instead? 1666 01:25:27,230 --> 01:25:32,120 Well, then I should return the add.html file again. 1667 01:25:32,120 --> 01:25:35,800 But instead of providing the form back to them, a new form back to them, 1668 01:25:35,800 --> 01:25:39,400 I'm going to send back the existing form data back to them. 1669 01:25:39,400 --> 01:25:44,507 So we can display information about any errors that might have come up as well. 1670 01:25:44,507 --> 01:25:45,840 So what does this now look like? 1671 01:25:45,840 --> 01:25:46,660 Let's show an example. 1672 01:25:46,660 --> 01:25:50,000 And then I'll go back to the code so you can see in better detail how it works. 1673 01:25:50,000 --> 01:25:51,670 Here is task/add. 1674 01:25:51,670 --> 01:25:54,760 Recall that if I type in a task like check email 1675 01:25:54,760 --> 01:25:58,210 and a priority that's not valid, that's out of range, like 11, 1676 01:25:58,210 --> 01:26:01,540 and press Submit, it says, value must be less than or equal to 10. 1677 01:26:01,540 --> 01:26:05,200 But let's now imagine a situation where the client and server are 1678 01:26:05,200 --> 01:26:08,890 validating different things, that maybe I've now decided, you know what? 1679 01:26:08,890 --> 01:26:13,300 Priority, instead of being from 1 to 10, can only be from one to five. 1680 01:26:13,300 --> 01:26:15,850 That's now the valid range for priorities. 1681 01:26:15,850 --> 01:26:21,070 But this client, which is still an older version of the page, doesn't know this. 1682 01:26:21,070 --> 01:26:23,650 So it thinks that a priority of eight is still valid. 1683 01:26:23,650 --> 01:26:26,350 And it's going to past client side validation. 1684 01:26:26,350 --> 01:26:27,910 But now I press Submit. 1685 01:26:27,910 --> 01:26:29,560 And the server is going to process it. 1686 01:26:29,560 --> 01:26:33,430 And because it's invalid, it gives me back the form and gives me an error. 1687 01:26:33,430 --> 01:26:37,920 Ensure this value is less than or equal to five. 1688 01:26:37,920 --> 01:26:40,630 And so this now is why we generally want both client- 1689 01:26:40,630 --> 01:26:45,490 and server-side validation, to make sure that the data we ultimately get 1690 01:26:45,490 --> 01:26:47,620 is going to be accurate and it's going to be clean, 1691 01:26:47,620 --> 01:26:50,380 matching whatever specification we sent out when we were 1692 01:26:50,380 --> 01:26:53,930 creating that form for the first time. 1693 01:26:53,930 --> 01:26:55,750 And so for the purposes of now, we're not 1694 01:26:55,750 --> 01:26:57,670 going to really worry about priority too much, because we just 1695 01:26:57,670 --> 01:26:59,137 really care about what the task is. 1696 01:26:59,137 --> 01:27:00,970 But just know that if you wanted a form that 1697 01:27:00,970 --> 01:27:05,080 had multiple fields, that you can add additional fields to this form input 1698 01:27:05,080 --> 01:27:06,590 as well. 1699 01:27:06,590 --> 01:27:08,890 So now we've added some application logic 1700 01:27:08,890 --> 01:27:11,980 to our route that checks to make sure that the form is valid or not. 1701 01:27:11,980 --> 01:27:14,570 If we take a look at what the add function is really doing, 1702 01:27:14,570 --> 01:27:16,680 we're checking if the request method is POST, 1703 01:27:16,680 --> 01:27:19,330 meaning if the user submitted some form data. 1704 01:27:19,330 --> 01:27:21,940 Then we figure out all the data they submitted and save it 1705 01:27:21,940 --> 01:27:23,650 inside this form variable. 1706 01:27:23,650 --> 01:27:25,210 We check to see if the form is valid. 1707 01:27:25,210 --> 01:27:26,860 Did they actually provide a task? 1708 01:27:26,860 --> 01:27:29,950 Are they providing all the necessary data in the right format? 1709 01:27:29,950 --> 01:27:33,730 If so, then we get the task and add it to the list of tasks. 1710 01:27:33,730 --> 01:27:35,950 Otherwise, if the form is not valid, then we 1711 01:27:35,950 --> 01:27:39,130 go ahead and render that same add.html file back to them, 1712 01:27:39,130 --> 01:27:41,800 but we pass in the form that they submitted so that they 1713 01:27:41,800 --> 01:27:43,330 can see all of the errors they made. 1714 01:27:43,330 --> 01:27:47,530 They can make modifications to their own form submission if they'd like to. 1715 01:27:47,530 --> 01:27:50,080 And then otherwise, meaning if the request method wasn't POST 1716 01:27:50,080 --> 01:27:54,500 at all, if the user just tried to get the page rather than submit data to it, 1717 01:27:54,500 --> 01:27:57,170 then we're just going to render to them an empty form. 1718 01:27:57,170 --> 01:27:59,382 And this sort of paradigm is actually quite common 1719 01:27:59,382 --> 01:28:01,340 when we're dealing with requests and responses, 1720 01:28:01,340 --> 01:28:04,310 that oftentimes pages that have forms will want you to first 1721 01:28:04,310 --> 01:28:07,160 be able to get that form via the GET method, 1722 01:28:07,160 --> 01:28:09,680 to just get the page in order to display the contents, 1723 01:28:09,680 --> 01:28:13,970 but those routes will also often support a POST method, where you can post data 1724 01:28:13,970 --> 01:28:17,480 to those routes in order to say, I would like to now submit data 1725 01:28:17,480 --> 01:28:20,750 to a particular route in order to get some sort of results some, 1726 01:28:20,750 --> 01:28:23,360 sort of addition to a list of tasks, transferring money 1727 01:28:23,360 --> 01:28:25,640 in a bank from one account to another, for example, 1728 01:28:25,640 --> 01:28:28,010 or something else entirely. 1729 01:28:28,010 --> 01:28:33,590 But watch what happens if I now try inside of /tasks/add to add a new task 1730 01:28:33,590 --> 01:28:36,380 that's valid, something like check email, for example. 1731 01:28:36,380 --> 01:28:37,850 I press Submit. 1732 01:28:37,850 --> 01:28:40,790 And all right, nothing quite seems to happen, 1733 01:28:40,790 --> 01:28:43,690 because I just get back this NewTaskForm. 1734 01:28:43,690 --> 01:28:46,460 But if I go back to view tasks, now, I see 1735 01:28:46,460 --> 01:28:48,830 that check email has been added to my list of tasks, 1736 01:28:48,830 --> 01:28:52,140 because the original list of tasks just foo, bar, baz. 1737 01:28:52,140 --> 01:28:54,080 And now we've added check email to it. 1738 01:28:54,080 --> 01:28:56,750 But this wasn't quite the behavior that I might have expected. 1739 01:28:56,750 --> 01:29:00,980 Maybe I wanted that, after I submitted the task form to add a new task, 1740 01:29:00,980 --> 01:29:04,690 I would like to be redirected back to this page as well. 1741 01:29:04,690 --> 01:29:07,880 And it turns out Django makes it easy for us to be able to redirect users 1742 01:29:07,880 --> 01:29:09,920 from one page to another. 1743 01:29:09,920 --> 01:29:14,450 In order to do that, after we add a new task to my list of tasks, 1744 01:29:14,450 --> 01:29:19,610 I'm going to return an HttpResponseRedirect 1745 01:29:19,610 --> 01:29:22,520 and redirect the user to a particular route. 1746 01:29:22,520 --> 01:29:26,130 I could redirect them to just, like, /tasks, for example. 1747 01:29:26,130 --> 01:29:30,440 But again, we generally try not to hardcode URLs into our application. 1748 01:29:30,440 --> 01:29:34,940 So better design would be to say, let me give you the name of the route 1749 01:29:34,940 --> 01:29:38,660 and go ahead and reverse engineer what the route actually is from that. 1750 01:29:38,660 --> 01:29:42,020 And so in order to do that, we can use the function called reverse built 1751 01:29:42,020 --> 01:29:49,820 into Django, and say tasks:index, to say figure out what the URL of the index 1752 01:29:49,820 --> 01:29:51,620 URL for the tasks app is. 1753 01:29:51,620 --> 01:29:54,290 And use that URL as the one that we ultimately 1754 01:29:54,290 --> 01:29:58,910 redirect to when we return this HttpResponseRedirect. 1755 01:29:58,910 --> 01:30:01,620 In order to use these, we need to import both of them. 1756 01:30:01,620 --> 01:30:05,930 So from the top, I'll say, from django.http import 1757 01:30:05,930 --> 01:30:09,380 HttpResponseRedirect. 1758 01:30:09,380 --> 01:30:14,990 And from Django.urls, I'll go an import reverse. 1759 01:30:14,990 --> 01:30:17,000 So now I've imported both of these. 1760 01:30:17,000 --> 01:30:20,840 And now the effect of this is that, after I submit a new task 1761 01:30:20,840 --> 01:30:23,060 and add it to my list of tasks, I'm going 1762 01:30:23,060 --> 01:30:27,152 to be redirected back to the index page of my tasks application. 1763 01:30:27,152 --> 01:30:30,110 And for good measure, I'll go ahead and start us off with an empty list 1764 01:30:30,110 --> 01:30:32,780 to get rid of the foo, bar, baz that we saw there originally. 1765 01:30:32,780 --> 01:30:36,150 So tasks start out as the empty list. 1766 01:30:36,150 --> 01:30:37,310 Now refresh the page. 1767 01:30:37,310 --> 01:30:40,040 I see no tasks in here by default. But if I 1768 01:30:40,040 --> 01:30:44,600 add a new task, I type in something like check email, press Submit, 1769 01:30:44,600 --> 01:30:46,370 I now see that added to the task list. 1770 01:30:46,370 --> 01:30:48,920 And I get redirected back to the task page. 1771 01:30:48,920 --> 01:30:52,670 I can add new task, something like do laundry, press Submit. 1772 01:30:52,670 --> 01:30:56,210 And that gets added to my task as well too. 1773 01:30:56,210 --> 01:30:58,730 So by maintaining was global variable called tasks 1774 01:30:58,730 --> 01:31:01,010 and updating it anytime I submit the form, 1775 01:31:01,010 --> 01:31:05,360 I've been able to dynamically grow this list of tasks inside of my application 1776 01:31:05,360 --> 01:31:09,780 and display all of those tasks here inside of my HTML page. 1777 01:31:09,780 --> 01:31:13,340 However, there is still one big problem with the application that I've built. 1778 01:31:13,340 --> 01:31:16,160 And it goes back to the idea that I've stored these tasks 1779 01:31:16,160 --> 01:31:18,170 inside of a global variable. 1780 01:31:18,170 --> 01:31:22,160 This variable is something the entire application has access to, 1781 01:31:22,160 --> 01:31:24,500 which means that anyone who visits my website 1782 01:31:24,500 --> 01:31:27,970 is going to be able to see the same exact list of tasks. 1783 01:31:27,970 --> 01:31:30,170 And we can simulate this by imagining someone else 1784 01:31:30,170 --> 01:31:32,930 visiting this URL, which I can simulate in Google Chrome 1785 01:31:32,930 --> 01:31:35,060 by opening an incognito window to simulate 1786 01:31:35,060 --> 01:31:38,210 a different session, a different person interacting with the page, 1787 01:31:38,210 --> 01:31:40,340 and going to the same URL. 1788 01:31:40,340 --> 01:31:43,760 What they see when, any incognito window, they go to the same URL, 1789 01:31:43,760 --> 01:31:46,070 is they see the exact same list of tasks. 1790 01:31:46,070 --> 01:31:50,990 Both me and another person see the same list, because there is just one tasks 1791 01:31:50,990 --> 01:31:53,600 variable across the entire application that 1792 01:31:53,600 --> 01:31:58,050 is shared among all of the requests that come in to that particular application. 1793 01:31:58,050 --> 01:32:00,410 And that's probably not what I want when I'm dealing 1794 01:32:00,410 --> 01:32:02,160 with something like a list of tasks. 1795 01:32:02,160 --> 01:32:05,600 I probably want it to be per user, such that if a different user visits 1796 01:32:05,600 --> 01:32:09,328 the page, they have their own list of tasks as well. 1797 01:32:09,328 --> 01:32:11,120 And so in order to do this, we'll introduce 1798 01:32:11,120 --> 01:32:13,730 the concept of sessions in Django, or in the web more 1799 01:32:13,730 --> 01:32:15,950 generally, where sessions are away way for, 1800 01:32:15,950 --> 01:32:18,860 one, Django to be able to remember who you are, such 1801 01:32:18,860 --> 01:32:22,640 that on subsequent visits, it remembers who you are and knows who you are. 1802 01:32:22,640 --> 01:32:25,700 But more importantly, it's able to then store 1803 01:32:25,700 --> 01:32:27,520 data about your particular session. 1804 01:32:27,520 --> 01:32:30,470 It's able to store your user ID or information about you. 1805 01:32:30,470 --> 01:32:34,710 Or in this case, it's able to store all of your tasks. 1806 01:32:34,710 --> 01:32:38,000 And so in order to take advantage of sessions, 1807 01:32:38,000 --> 01:32:41,240 instead of having a global variable called tasks, 1808 01:32:41,240 --> 01:32:44,570 we're going to go ahead and delete this and instead store 1809 01:32:44,570 --> 01:32:48,620 tasks inside of the user's session. 1810 01:32:48,620 --> 01:32:52,610 So inside the index route, I'll include a line like this. 1811 01:32:52,610 --> 01:32:58,502 I'll check to see if tasks is not in request.session, 1812 01:32:58,502 --> 01:33:01,460 meaning if I look inside the session-- and you can think of the session 1813 01:33:01,460 --> 01:33:05,210 as like a big dictionary representing all the data we have on file inside 1814 01:33:05,210 --> 01:33:07,200 the session about the user-- 1815 01:33:07,200 --> 01:33:09,860 and if tasks is not in that session, well, 1816 01:33:09,860 --> 01:33:18,750 let me add to request.session tasks and set that equal to the empty list. 1817 01:33:18,750 --> 01:33:22,310 And so what I've done here is I'm looking inside of the session. 1818 01:33:22,310 --> 01:33:25,250 I'm looking inside the session to see is there already 1819 01:33:25,250 --> 01:33:27,400 a list of tasks in that session. 1820 01:33:27,400 --> 01:33:30,835 And if there isn't, if there isn't already a list of tasks in the session, 1821 01:33:30,835 --> 01:33:32,210 well, then I'd like to create it. 1822 01:33:32,210 --> 01:33:34,820 Then I'd like to set request.session square bracket 1823 01:33:34,820 --> 01:33:37,610 tasks equal to the empty list. 1824 01:33:37,610 --> 01:33:39,920 If the user doesn't already have a list of tasks, 1825 01:33:39,920 --> 01:33:44,030 go ahead and give them an empty list of tasks. 1826 01:33:44,030 --> 01:33:47,540 And now here in tasks, instead of rendering the variable tasks which 1827 01:33:47,540 --> 01:33:52,760 no longer exists, I'll render request.session tasks 1828 01:33:52,760 --> 01:33:58,520 to pass in that list of tasks to this particular template. 1829 01:33:58,520 --> 01:34:02,020 And now, index.html, we're still looping over that list of tasks. 1830 01:34:02,020 --> 01:34:08,360 And now we'll see if I go back to Tasks, I see, no such table django_session. 1831 01:34:08,360 --> 01:34:09,860 So this is a bit of a strange error. 1832 01:34:09,860 --> 01:34:12,653 What's going on here, no such table django_session? 1833 01:34:12,653 --> 01:34:14,570 Well it turns out, as we'll see in the future, 1834 01:34:14,570 --> 01:34:17,558 Django tends to store data inside of tables. 1835 01:34:17,558 --> 01:34:19,850 And we haven't yet gotten to what that ultimately means 1836 01:34:19,850 --> 01:34:23,000 or how to manipulate or interact with data stored inside of tables. 1837 01:34:23,000 --> 01:34:26,750 But Django stores data about sessions that would happen inside of a table 1838 01:34:26,750 --> 01:34:29,750 by default. And you can change to have Django store data about sessions 1839 01:34:29,750 --> 01:34:30,440 elsewhere. 1840 01:34:30,440 --> 01:34:33,170 But ultimately, Django is keeping data about who you are 1841 01:34:33,170 --> 01:34:34,490 and what your tasks are. 1842 01:34:34,490 --> 01:34:36,530 And that data needs to be stored somewhere. 1843 01:34:36,530 --> 01:34:40,070 And by default, Django wants to store it inside of a table. 1844 01:34:40,070 --> 01:34:41,990 And right now that table doesn't exist. 1845 01:34:41,990 --> 01:34:43,710 So we need to create it. 1846 01:34:43,710 --> 01:34:46,850 And the way to give Django access to that table that it wants to create, 1847 01:34:46,850 --> 01:34:49,100 that it has been waiting to create, but it hasn't yet, 1848 01:34:49,100 --> 01:34:55,110 is to run this command inside the terminal, python manage.py migrate. 1849 01:34:55,110 --> 01:34:58,730 And we'll learn more about what that means in terms of what a migration is 1850 01:34:58,730 --> 01:35:01,590 and what it means to migrate data into a database. 1851 01:35:01,590 --> 01:35:04,460 But for now, just know that python manage.py migrate 1852 01:35:04,460 --> 01:35:07,760 will allow us to create all of the default tables 1853 01:35:07,760 --> 01:35:09,088 inside of Django's database. 1854 01:35:09,088 --> 01:35:12,005 Next time we'll take a look at actually creating databases of our own, 1855 01:35:12,005 --> 01:35:14,930 and adding tables of our own to store our own custom data. 1856 01:35:14,930 --> 01:35:18,740 But Django has some initial default tables that it wants to create. 1857 01:35:18,740 --> 01:35:23,190 And running python manage.py migrate allows for those tables to be created. 1858 01:35:23,190 --> 01:35:26,720 Now that those exist, I first need to run the server, so python 1859 01:35:26,720 --> 01:35:30,910 manage.py runserver. 1860 01:35:30,910 --> 01:35:31,970 I load the page. 1861 01:35:31,970 --> 01:35:33,557 And here now is my list of tasks. 1862 01:35:33,557 --> 01:35:36,140 There is nothing here, so when I loop over all the list items, 1863 01:35:36,140 --> 01:35:38,960 it's sort of empty, which maybe isn't the best user 1864 01:35:38,960 --> 01:35:40,640 experience or user design. 1865 01:35:40,640 --> 01:35:43,700 So what I might want to do is add an additional condition. 1866 01:35:43,700 --> 01:35:46,520 It turns out inside of index.html, whenever 1867 01:35:46,520 --> 01:35:49,490 I have a for loop inside of a Django template, 1868 01:35:49,490 --> 01:35:53,960 I can also add an empty condition to say, if I run the for loop 1869 01:35:53,960 --> 01:35:57,050 but it doesn't run at all because the sequence is empty, well, 1870 01:35:57,050 --> 01:36:00,092 then let me just say, like, no tasks, for example. 1871 01:36:00,092 --> 01:36:02,300 And this is just a nice to have feature of the Django 1872 01:36:02,300 --> 01:36:05,990 language that just makes it easy for us to be able to deal with situations 1873 01:36:05,990 --> 01:36:10,020 where we're iterating over a list and there is nothing in that list. 1874 01:36:10,020 --> 01:36:14,780 So now I refresh the page, No tasks, exactly what I would expect to see. 1875 01:36:14,780 --> 01:36:17,780 And now this index route seems to be working fine. 1876 01:36:17,780 --> 01:36:22,460 What now needs to change when it comes to adding a new task? 1877 01:36:22,460 --> 01:36:25,940 Well, rather than append the task to my list of tasks, let me go ahead 1878 01:36:25,940 --> 01:36:30,570 and say, request.session tasks, because that is my list of tasks. 1879 01:36:30,570 --> 01:36:34,320 And let me add to request.session tasks this new task. 1880 01:36:34,320 --> 01:36:36,680 And I'm adding to it a new-- sort of adding 1881 01:36:36,680 --> 01:36:41,510 a list to the end of it that just contains the one new task 1882 01:36:41,510 --> 01:36:43,160 that I got from the form. 1883 01:36:43,160 --> 01:36:46,490 So when the form is submitted, we check to make sure the form is valid. 1884 01:36:46,490 --> 01:36:48,530 We get the task that the user submitted. 1885 01:36:48,530 --> 01:36:51,290 And we append that to the list of tasks that's 1886 01:36:51,290 --> 01:36:55,820 already stored inside of the session before redirecting the user back 1887 01:36:55,820 --> 01:36:58,460 to the index page. 1888 01:36:58,460 --> 01:36:59,720 So let's give it a try. 1889 01:36:59,720 --> 01:37:01,880 We'll go back to this URL, back to Tasks. 1890 01:37:01,880 --> 01:37:04,350 I see I have no tasks initially. 1891 01:37:04,350 --> 01:37:07,730 And now I can go ahead and add a new task, type in something 1892 01:37:07,730 --> 01:37:12,100 like check email, press Submit. 1893 01:37:12,100 --> 01:37:13,420 And right, now that's added. 1894 01:37:13,420 --> 01:37:16,790 Add our new task, something like do laundry, Submit. 1895 01:37:16,790 --> 01:37:18,740 And now both of those tasks are there. 1896 01:37:18,740 --> 01:37:23,540 But now, importantly, if you imagine someone in an incognito window 1897 01:37:23,540 --> 01:37:27,500 or on a different computer visiting that same website going back to this page, 1898 01:37:27,500 --> 01:37:30,560 what they see is an entirely different list of tasks 1899 01:37:30,560 --> 01:37:32,552 because they have a different session. 1900 01:37:32,552 --> 01:37:34,260 Their sessions are determined by cookies, 1901 01:37:34,260 --> 01:37:36,427 these little hand stamps that help the browser to be 1902 01:37:36,427 --> 01:37:39,580 able to give some information to Django's web server to say, here 1903 01:37:39,580 --> 01:37:42,360 is who I am, so Django knows what data to show you. 1904 01:37:42,360 --> 01:37:44,420 And in this case, my original case, Django 1905 01:37:44,420 --> 01:37:47,030 knows who I am, knows to show me these tasks. 1906 01:37:47,030 --> 01:37:50,890 And in this case, it's a different user in an incognito window, in this case. 1907 01:37:50,890 --> 01:37:56,420 And so what they see is a list that has no tasks inside it at all. 1908 01:37:56,420 --> 01:37:59,590 So now we've really just scratched the surface of what Django has to offer. 1909 01:37:59,590 --> 01:38:03,160 But we see now the ability it has to be able to create dynamic web 1910 01:38:03,160 --> 01:38:03,820 applications. 1911 01:38:03,820 --> 01:38:08,050 Instead of just displaying HTML and CSS that's the same every time, 1912 01:38:08,050 --> 01:38:10,120 using Django, we now have the ability to be 1913 01:38:10,120 --> 01:38:14,740 able to generate programmatically custom HTML and CSS, either saying hello 1914 01:38:14,740 --> 01:38:18,220 to a person's name based on what name is provided inside of URL, 1915 01:38:18,220 --> 01:38:20,500 or the ability to say-- to check the current date 1916 01:38:20,500 --> 01:38:24,190 and conditionally display something if the date is one thing versus another, 1917 01:38:24,190 --> 01:38:26,740 and the ability to store data on a session basis, 1918 01:38:26,740 --> 01:38:29,650 to be able to store information about a user's to do list, 1919 01:38:29,650 --> 01:38:31,810 for example, such that on subsequent visits, 1920 01:38:31,810 --> 01:38:33,640 they can see their list of things they need 1921 01:38:33,640 --> 01:38:36,718 to do with a different list for each of these possible users. 1922 01:38:36,718 --> 01:38:39,510 And here really is just the beginning of where Django has to offer. 1923 01:38:39,510 --> 01:38:43,090 Where Django gets very powerful is when it comes towards storing data inside 1924 01:38:43,090 --> 01:38:45,430 of databases, and manipulating that data, 1925 01:38:45,430 --> 01:38:48,125 and interacting with that data in various different ways. 1926 01:38:48,125 --> 01:38:51,250 And that's ultimately where a lot of the power of a web framework like this 1927 01:38:51,250 --> 01:38:52,330 ultimately comes in. 1928 01:38:52,330 --> 01:38:53,898 We'll explore more of that next time. 1929 01:38:53,898 --> 01:38:55,690 But for now, that was just a look at Django 1930 01:38:55,690 --> 01:38:59,050 and how we can use it to be able to build these sorts of web applications. 1931 01:38:59,050 --> 01:39:01,330 This was "Web Programming with Python and JavaScript." 1932 01:39:01,330 --> 01:39:03,420 We'll see you next time. 1933 01:39:03,420 --> 01:39:04,000