1 00:00:00,000 --> 00:00:03,472 [MUSIC PLAYING] 2 00:00:03,472 --> 00:01:01,550 3 00:01:01,550 --> 00:01:03,830 DAVID J. MALAN: All right, this is CS50. 4 00:01:03,830 --> 00:01:06,230 And this is already week nine. 5 00:01:06,230 --> 00:01:08,540 And this means this is the week where we synthesize 6 00:01:08,540 --> 00:01:13,670 the past several weeks, from Python to SQL to HTML to CSS to JavaScript. 7 00:01:13,670 --> 00:01:16,580 Each of those languages that we've rather looked at in isolation 8 00:01:16,580 --> 00:01:19,790 now rather come together toward an end of just making 9 00:01:19,790 --> 00:01:25,980 more modern applications, be them web based or be they mobile based as well. 10 00:01:25,980 --> 00:01:29,060 So, up until now, we've been using, to serve all of the web stuff 11 00:01:29,060 --> 00:01:31,640 that you've done, this program called HTTP server. 12 00:01:31,640 --> 00:01:32,900 Now, this isn't a CS50 thing. 13 00:01:32,900 --> 00:01:36,260 It's just a standard program that we've installed into Codespaces. 14 00:01:36,260 --> 00:01:39,450 So it's a Linux program that just runs your own web server. 15 00:01:39,450 --> 00:01:42,140 And it allows you to run it on nonstandard ports. 16 00:01:42,140 --> 00:01:44,420 For instance, you've been using it on port 80, 80 17 00:01:44,420 --> 00:01:49,398 only because Codespaces is using 80 and 443, recall, which were the defaults. 18 00:01:49,398 --> 00:01:51,440 But, up until now, this program's purpose in life 19 00:01:51,440 --> 00:01:55,520 was just to serve static content, so like web pages written in HTML, 20 00:01:55,520 --> 00:01:59,100 maybe some CSS, maybe some JavaScript that you wrote in advance 21 00:01:59,100 --> 00:02:02,040 that just never really changes until such time 22 00:02:02,040 --> 00:02:05,640 as you log back into the server, save the file after making some edits, 23 00:02:05,640 --> 00:02:07,320 and then someone reloads their page. 24 00:02:07,320 --> 00:02:11,640 But, of course, the web that we all use today, be it Gmail or any website, 25 00:02:11,640 --> 00:02:13,240 is so much more interactive. 26 00:02:13,240 --> 00:02:15,240 When you search for something on Google, there's 27 00:02:15,240 --> 00:02:18,960 no Google engineer that, in advance, has written up an HTML 28 00:02:18,960 --> 00:02:22,470 page containing a list of 10 cats or 10 dogs 29 00:02:22,470 --> 00:02:26,430 or 10 birds just waiting for someone to search for that particular keyword. 30 00:02:26,430 --> 00:02:28,200 Rather, there's some database involved. 31 00:02:28,200 --> 00:02:30,060 The HTML's being dynamically generated. 32 00:02:30,060 --> 00:02:31,860 And it's all indeed very dynamic. 33 00:02:31,860 --> 00:02:34,980 So whereas, last week, we focused on websites, 34 00:02:34,980 --> 00:02:38,070 this week we'll focus really on web applications. 35 00:02:38,070 --> 00:02:40,590 And, really, the key difference is just that applications 36 00:02:40,590 --> 00:02:45,120 take input and produce output whereas websites are really generally thought 37 00:02:45,120 --> 00:02:45,960 of as static. 38 00:02:45,960 --> 00:02:46,920 But it's a blurry line. 39 00:02:46,920 --> 00:02:48,795 It's not necessarily a technical distinction. 40 00:02:48,795 --> 00:02:51,070 But today things start to get much more interactive. 41 00:02:51,070 --> 00:02:53,648 So we're not going to be able to use HTTP server alone. 42 00:02:53,648 --> 00:02:56,190 We're going to need something a little smarter that knows how 43 00:02:56,190 --> 00:02:59,010 to take input from users via the URL. 44 00:02:59,010 --> 00:03:02,610 So, in fact, let's look at some sample URLs that we looked at last week 45 00:03:02,610 --> 00:03:05,980 just to remind ourselves of some of the jargon and really the syntax. 46 00:03:05,980 --> 00:03:09,360 So here's a standard URL and Flask, recall, 47 00:03:09,360 --> 00:03:11,280 is just the default page on the server. 48 00:03:11,280 --> 00:03:15,340 And, usually, the file is called index.html by convention. 49 00:03:15,340 --> 00:03:18,090 But, depending on the operating system or web server you're using, 50 00:03:18,090 --> 00:03:19,380 it could be a different file. 51 00:03:19,380 --> 00:03:22,720 But index.html is probably the most common default. 52 00:03:22,720 --> 00:03:26,490 So you could explicitly state the file name on most web servers 53 00:03:26,490 --> 00:03:29,670 like file.html, index.html, whatever it is. 54 00:03:29,670 --> 00:03:33,010 You can have folders or directories on a web server. 55 00:03:33,010 --> 00:03:36,600 And this would imply that your index.html file is 56 00:03:36,600 --> 00:03:39,210 in a folder called folder in this case. 57 00:03:39,210 --> 00:03:44,950 Or you can have a folder/file or even folder/folder/folder/file and so forth. 58 00:03:44,950 --> 00:03:50,260 So this is a very direct mapping from the URL to the file system, 59 00:03:50,260 --> 00:03:52,380 so to speak, like the hard drive on the server. 60 00:03:52,380 --> 00:03:55,530 But, today, we're going to do, like most computer scientists tend to do, 61 00:03:55,530 --> 00:03:57,420 is more of an abstraction of this. 62 00:03:57,420 --> 00:03:59,130 This is how URLs look. 63 00:03:59,130 --> 00:04:02,220 But it turns out, once you control the web server with code, 64 00:04:02,220 --> 00:04:06,330 you don't need things to line up with actual file names and files. 65 00:04:06,330 --> 00:04:08,740 You can really put your content wherever you want. 66 00:04:08,740 --> 00:04:10,740 And so, in general, we're going to focus today 67 00:04:10,740 --> 00:04:14,250 on just thinking of everything after the domain name as a path, 68 00:04:14,250 --> 00:04:15,390 generally speaking. 69 00:04:15,390 --> 00:04:19,019 And a synonym for this, in the context of the web, would also just be a route. 70 00:04:19,019 --> 00:04:23,040 So a route is just some number of letters, maybe some slashes, maybe 71 00:04:23,040 --> 00:04:26,920 a file extension that refers to some part of your application. 72 00:04:26,920 --> 00:04:30,780 But, more interestingly-- and this is what makes things an application 73 00:04:30,780 --> 00:04:32,280 and not just a static website-- 74 00:04:32,280 --> 00:04:35,670 recall, that this is how websites can take input 75 00:04:35,670 --> 00:04:40,590 in the form of URLs, a question mark, then key equals value pair. 76 00:04:40,590 --> 00:04:42,870 Or if you want two or maybe three or four, 77 00:04:42,870 --> 00:04:44,820 you separate them with ampersands. 78 00:04:44,820 --> 00:04:46,110 But that's kind of it. 79 00:04:46,110 --> 00:04:48,627 The web today is going to work just like it did last week, 80 00:04:48,627 --> 00:04:51,210 but we're going to start leveraging these primitives in a much 81 00:04:51,210 --> 00:04:53,470 more powerful, more interactive way. 82 00:04:53,470 --> 00:04:56,430 So here, recall, is what might be inside of the virtual envelope 83 00:04:56,430 --> 00:04:58,830 when you search for something on google.com. 84 00:04:58,830 --> 00:05:02,070 It's going to request of the web server/search, which 85 00:05:02,070 --> 00:05:05,282 is the name, by convention, of Google's search application 86 00:05:05,282 --> 00:05:07,990 because they've got a lot of businesses running on their servers. 87 00:05:07,990 --> 00:05:11,610 And if you have ?q=cats, this is essentially the message, 88 00:05:11,610 --> 00:05:15,060 the HTTP message that would've been in last week's virtual envelope when I 89 00:05:15,060 --> 00:05:16,500 searched for cats. 90 00:05:16,500 --> 00:05:18,690 Hopefully, you get back a response from the server 91 00:05:18,690 --> 00:05:20,220 containing those actual cats. 92 00:05:20,220 --> 00:05:23,940 But, again, there's probably a lot more logic going on with a database 93 00:05:23,940 --> 00:05:27,480 and somehow generating that HTML that Google is doing for us. 94 00:05:27,480 --> 00:05:30,240 So we, today, are going to introduce really 95 00:05:30,240 --> 00:05:33,300 what's called a framework or technically a microframework, which 96 00:05:33,300 --> 00:05:36,450 means it's relatively small versus alternatives that are out there. 97 00:05:36,450 --> 00:05:37,800 And it's called Flask. 98 00:05:37,800 --> 00:05:40,350 So Flask is really a third-party library-- 99 00:05:40,350 --> 00:05:42,420 and it's popular in the Python world-- that's 100 00:05:42,420 --> 00:05:46,260 just going to make it easier to implement web applications using 101 00:05:46,260 --> 00:05:46,920 Python. 102 00:05:46,920 --> 00:05:47,940 There are alternatives. 103 00:05:47,940 --> 00:05:50,190 A very popular one is called Django, which some of you 104 00:05:50,190 --> 00:05:51,310 might've heard of before. 105 00:05:51,310 --> 00:05:54,510 And there's dozens of others in decreasing popularity daresay. 106 00:05:54,510 --> 00:05:57,870 But Flask is among the most popular microframeworks, 107 00:05:57,870 --> 00:06:00,900 which means you can really just solve a few problems pretty 108 00:06:00,900 --> 00:06:04,350 simply without feeling like you're learning some third-party library 109 00:06:04,350 --> 00:06:06,870 as much as you are learning concepts that transcend 110 00:06:06,870 --> 00:06:08,560 one particular implementation. 111 00:06:08,560 --> 00:06:11,850 So we're going to introduce you to a framework called Flask. 112 00:06:11,850 --> 00:06:13,560 And that's going to allow us, ultimately, 113 00:06:13,560 --> 00:06:17,520 to start web applications not by running HTTP server today 114 00:06:17,520 --> 00:06:20,970 but literally running flask space run. 115 00:06:20,970 --> 00:06:23,370 So this framework literally gives you a command, 116 00:06:23,370 --> 00:06:26,100 on your Mac, your PC, your codespace once it's installed, 117 00:06:26,100 --> 00:06:28,170 that allows you to start a web server by running 118 00:06:28,170 --> 00:06:30,858 flask run instead of HTTP server. 119 00:06:30,858 --> 00:06:33,150 So, again, last week, it was all about static websites. 120 00:06:33,150 --> 00:06:36,370 This week, it's all about dynamic websites instead. 121 00:06:36,370 --> 00:06:39,180 And, by framework, we really generally mean-- not just 122 00:06:39,180 --> 00:06:42,060 using some third-party code but third-party conventions 123 00:06:42,060 --> 00:06:45,270 that just some human or humans decided is the way 124 00:06:45,270 --> 00:06:46,770 you will build your applications. 125 00:06:46,770 --> 00:06:50,790 Usually, this is just based on lessons learned after making application 126 00:06:50,790 --> 00:06:51,660 after application. 127 00:06:51,660 --> 00:06:53,787 Humans around the internet realized, you know what? 128 00:06:53,787 --> 00:06:57,120 We should probably standardize the names of our files, the names of our folders, 129 00:06:57,120 --> 00:06:58,550 and how things are laid out. 130 00:06:58,550 --> 00:07:01,150 And so, even though this is not the only way to do things, 131 00:07:01,150 --> 00:07:05,620 the way that Flask prescribes that we programmers do things is this. 132 00:07:05,620 --> 00:07:10,540 We'll, starting today, always have a Python program called app.py 133 00:07:10,540 --> 00:07:12,280 by convention, like in our folder. 134 00:07:12,280 --> 00:07:15,730 And we're going to have a folder called templates, inside of which 135 00:07:15,730 --> 00:07:19,937 is any of the actual HTML, maybe CSS, maybe JavaScript that we write. 136 00:07:19,937 --> 00:07:22,270 But you can actually put some of that content elsewhere. 137 00:07:22,270 --> 00:07:24,700 And, in fact, you'll see there's going to be generally 138 00:07:24,700 --> 00:07:29,180 two other files or folders you'll see me create or me use today. 139 00:07:29,180 --> 00:07:33,040 One is called requirements.txt, which is literally just a simple text 140 00:07:33,040 --> 00:07:37,330 file wherein you specify one per line what third-party libraries 141 00:07:37,330 --> 00:07:38,170 you want to use. 142 00:07:38,170 --> 00:07:41,890 This file just makes it easy to install those libraries with a command. 143 00:07:41,890 --> 00:07:44,260 And then, lastly, a static folder. 144 00:07:44,260 --> 00:07:50,050 And it's in this folder that you put images or .css files or .js files, 145 00:07:50,050 --> 00:07:53,440 literally files that are meant to be static that you might change them once 146 00:07:53,440 --> 00:07:55,610 in a while, but they're not changing every day. 147 00:07:55,610 --> 00:07:58,540 And so you just want to isolate them to a particular folder. 148 00:07:58,540 --> 00:07:59,590 Why is it this way? 149 00:07:59,590 --> 00:08:03,250 Eh, a bunch of humans decided this feels like a clean solution. 150 00:08:03,250 --> 00:08:06,610 But reasonable people will disagree and different frameworks lay things out 151 00:08:06,610 --> 00:08:07,370 differently. 152 00:08:07,370 --> 00:08:11,410 So, when using a framework for the first time, you take a class or read a book 153 00:08:11,410 --> 00:08:12,920 or read the documentation. 154 00:08:12,920 --> 00:08:17,060 And it will essentially guide you to how you should lay out your application. 155 00:08:17,060 --> 00:08:20,530 So let's go ahead and do exactly that and make an application that quite 156 00:08:20,530 --> 00:08:24,100 simply and, by design, underwhelmingly implements Hello, world. 157 00:08:24,100 --> 00:08:27,040 But, rather than do this statically, let me 158 00:08:27,040 --> 00:08:29,560 do it in a way that starts to use this framework so 159 00:08:29,560 --> 00:08:32,440 that, in the next version of it, we can actually take user input 160 00:08:32,440 --> 00:08:34,750 and have it say not Hello, world but maybe Hello, 161 00:08:34,750 --> 00:08:37,720 David, or Hello, Yulia or anyone else. 162 00:08:37,720 --> 00:08:40,480 All right, so let me go over here to VS Code. 163 00:08:40,480 --> 00:08:44,680 And let me go ahead, and, initially, let me start with the familiar. 164 00:08:44,680 --> 00:08:49,510 And let me go ahead and start by simply creating the HTML page that I really 165 00:08:49,510 --> 00:08:53,000 want to show to my visitors when they visit my application. 166 00:08:53,000 --> 00:08:56,290 So I'm going to go ahead and, somewhat incorrectly, initially, 167 00:08:56,290 --> 00:08:58,540 but just to make a point, I'm going to go ahead and do 168 00:08:58,540 --> 00:09:02,353 code index.html to open up a new tab. 169 00:09:02,353 --> 00:09:05,020 I'll hide my terminal window just to give myself some more room. 170 00:09:05,020 --> 00:09:08,910 And then really fast I'm going to type out some boilerplate HTML. 171 00:09:08,910 --> 00:09:14,970 So DOCTYPE html, just like last week, open bracket html Lang equals en just 172 00:09:14,970 --> 00:09:18,510 to tell the VS Code that I'm-- 173 00:09:18,510 --> 00:09:20,880 to tell the web that I'm largely using English here. 174 00:09:20,880 --> 00:09:24,073 In the head of my page, I'm going to have, of course, the title of the page. 175 00:09:24,073 --> 00:09:26,490 And I'll keep it simple and just say something like hello. 176 00:09:26,490 --> 00:09:30,900 But just so that this website actually renders nicely on mobile devices, 177 00:09:30,900 --> 00:09:33,540 I'm going to use one of those meta tags we talked briefly 178 00:09:33,540 --> 00:09:39,360 about last week whereby if I say meta name equals viewport-- 179 00:09:39,360 --> 00:09:43,050 and viewport refers to just the big rectangular region of your browser-- 180 00:09:43,050 --> 00:09:48,510 the content of this meta tag is going to be initial scale 181 00:09:48,510 --> 00:09:51,750 equals 1 and width equals device width. 182 00:09:51,750 --> 00:09:54,660 I always have to copy-paste this or look it up myself. 183 00:09:54,660 --> 00:09:57,780 But this line here essentially tells the browser 184 00:09:57,780 --> 00:10:01,090 that, no matter how wide the device is, whether it's 185 00:10:01,090 --> 00:10:05,530 a laptop or desktop or maybe a vertical cell phone or tablet, 186 00:10:05,530 --> 00:10:09,070 size the viewport to that device. 187 00:10:09,070 --> 00:10:12,373 Otherwise, your website might look super small on mobile devices 188 00:10:12,373 --> 00:10:15,040 if you don't use this tag to tell the browser, take into account 189 00:10:15,040 --> 00:10:18,250 the actual device width rather than shrinking the 12-point font 190 00:10:18,250 --> 00:10:20,500 to something that's hard for folks to read. 191 00:10:20,500 --> 00:10:24,250 So, for now, I'm just going to generally copy-paste that or type it out 192 00:10:24,250 --> 00:10:27,062 from my printout here. 193 00:10:27,062 --> 00:10:29,270 All right, beyond that, we need the body of the page. 194 00:10:29,270 --> 00:10:30,228 We'll keep that simple. 195 00:10:30,228 --> 00:10:33,800 So body, and then, in here, hello comma world. 196 00:10:33,800 --> 00:10:35,740 So that's it for my website thus far. 197 00:10:35,740 --> 00:10:36,430 It's static. 198 00:10:36,430 --> 00:10:39,500 Nothing about this is going to incorporate my name or anyone else's. 199 00:10:39,500 --> 00:10:43,540 So I could technically use HTTP server to serve up this web page, 200 00:10:43,540 --> 00:10:46,960 open it in a browser, and I would see the actual contents. 201 00:10:46,960 --> 00:10:49,510 But let's instead create a bit of work for us 202 00:10:49,510 --> 00:10:53,290 and sort of overengineer this problem but to set the stage for actually 203 00:10:53,290 --> 00:10:56,210 taking in dynamic input like a user's name. 204 00:10:56,210 --> 00:10:57,880 So let me go ahead and to do this. 205 00:10:57,880 --> 00:11:01,120 I'm going to go ahead and open my terminal window again. 206 00:11:01,120 --> 00:11:04,210 I'm going to close the index.html file. 207 00:11:04,210 --> 00:11:06,910 I'm going to make a new directory called templates, 208 00:11:06,910 --> 00:11:08,860 which, again, was the default folder name I 209 00:11:08,860 --> 00:11:11,230 mentioned that this framework expects. 210 00:11:11,230 --> 00:11:16,100 And I'm going to move index.html into that templates folder using mv, a Linux 211 00:11:16,100 --> 00:11:16,600 command. 212 00:11:16,600 --> 00:11:21,878 If you're more comfy, you can open up the file, the File Explorer at right. 213 00:11:21,878 --> 00:11:23,920 You'll see, in advance, I downloaded a directory. 214 00:11:23,920 --> 00:11:26,590 I'll occasionally borrow content from today called src9, 215 00:11:26,590 --> 00:11:28,090 but there is my templates folder. 216 00:11:28,090 --> 00:11:30,850 And you could click in the GUI in order to do 217 00:11:30,850 --> 00:11:32,565 what I just did at the command line. 218 00:11:32,565 --> 00:11:35,440 All right, and, after this, let's go ahead and create one other file, 219 00:11:35,440 --> 00:11:37,270 app.py. 220 00:11:37,270 --> 00:11:41,750 And, in app.py, let me go ahead now and do this. 221 00:11:41,750 --> 00:11:48,310 I'm going to import some functions that come with this framework called Flask. 222 00:11:48,310 --> 00:11:54,010 So I'm going to say from flask, in lowercase, import Flask, capital F, 223 00:11:54,010 --> 00:11:59,250 and also a function called render template and an object called request. 224 00:11:59,250 --> 00:12:00,083 Now, how to do this? 225 00:12:00,083 --> 00:12:02,667 You literally read the documentation, or you listen to someone 226 00:12:02,667 --> 00:12:04,830 like me tell you to begin your program this way. 227 00:12:04,830 --> 00:12:09,180 In the Flask framework comes three pieces of functionality 228 00:12:09,180 --> 00:12:12,780 that are just useful to incorporate into my own program as we're about to see. 229 00:12:12,780 --> 00:12:15,990 Here's the line of code via which I can tell this framework 230 00:12:15,990 --> 00:12:20,940 to treat my file, app.py, as indeed a web application. 231 00:12:20,940 --> 00:12:23,430 I create a variable, typically called app. 232 00:12:23,430 --> 00:12:27,000 I set that equal to the return value of calling 233 00:12:27,000 --> 00:12:31,110 this Flask function and pass in it, somewhat weirdly, 234 00:12:31,110 --> 00:12:32,920 the name of this file. 235 00:12:32,920 --> 00:12:37,660 So this is the only weird thing for now in that we haven't used this much, 236 00:12:37,660 --> 00:12:45,090 if at all. __name__ is a special variable in Python that literally 237 00:12:45,090 --> 00:12:49,120 refers to the current file's name, no matter what file name you gave it. 238 00:12:49,120 --> 00:12:51,000 So it's a nice way of referring to yourself 239 00:12:51,000 --> 00:12:54,665 without manually typing the file name, which might change down the line. 240 00:12:54,665 --> 00:12:56,290 And then, lastly, I'm going to do this. 241 00:12:56,290 --> 00:12:58,570 And this is one other piece of new syntax for now. 242 00:12:58,570 --> 00:13:02,830 I'm going to use an @ and say app.route. 243 00:13:02,830 --> 00:13:05,950 And then in quotes, as an argument to this route function, 244 00:13:05,950 --> 00:13:09,430 I'm going to specify the route for which I'm implementing some code 245 00:13:09,430 --> 00:13:12,010 / being the default, by convention. 246 00:13:12,010 --> 00:13:14,260 I'm going to define immediately below that a function 247 00:13:14,260 --> 00:13:16,310 that I can technically call anything I want, 248 00:13:16,310 --> 00:13:18,935 but I'm going to get in the habit of using reasonable defaults. 249 00:13:18,935 --> 00:13:21,423 So I'm going to call this function index by default. 250 00:13:21,423 --> 00:13:22,840 But that's not a hard requirement. 251 00:13:22,840 --> 00:13:25,570 And then, inside of this function, I'm simply 252 00:13:25,570 --> 00:13:31,270 going to return this, return "hello, world", quote, unquote. 253 00:13:31,270 --> 00:13:32,350 And that's it. 254 00:13:32,350 --> 00:13:36,400 This I now claim is a beginning of an actual web application. 255 00:13:36,400 --> 00:13:38,980 It looks a little magical or cryptic as to what's going on. 256 00:13:38,980 --> 00:13:43,630 But, per the jargon I introduced earlier, this function here app.route 257 00:13:43,630 --> 00:13:46,090 is defining a route for this application that 258 00:13:46,090 --> 00:13:48,475 implies that whenever a human visit slash 259 00:13:48,475 --> 00:13:51,490 on this application, what should happen is 260 00:13:51,490 --> 00:13:53,740 this function index should get called. 261 00:13:53,740 --> 00:13:56,020 And that function's purpose in life, at the moment, 262 00:13:56,020 --> 00:14:00,350 is just to return, quote, unquote, "hello, world", and that's it. 263 00:14:00,350 --> 00:14:02,060 So let me go ahead and do this. 264 00:14:02,060 --> 00:14:04,265 Let me open my terminal and just to keep everything 265 00:14:04,265 --> 00:14:06,640 clean because we're going to have a bunch of applications 266 00:14:06,640 --> 00:14:07,432 today in the works. 267 00:14:07,432 --> 00:14:09,790 I'm going to create one other folder called hello. 268 00:14:09,790 --> 00:14:16,370 And I'm going to move app.py and templates into that hello folder. 269 00:14:16,370 --> 00:14:18,910 So if I now type ls in my own personal account, 270 00:14:18,910 --> 00:14:22,850 I've got that folder hello and also src9, which I brought with me today. 271 00:14:22,850 --> 00:14:26,650 So if I now cd into hello and type ls again, 272 00:14:26,650 --> 00:14:29,410 I'll see the two things we just created together, app.py 273 00:14:29,410 --> 00:14:30,500 and the templates folder. 274 00:14:30,500 --> 00:14:33,520 And if I go one step further in ls templates itself, 275 00:14:33,520 --> 00:14:36,460 I should see, of course, index.html. 276 00:14:36,460 --> 00:14:39,490 All right, so a lot of steps to go through just to get started. 277 00:14:39,490 --> 00:14:42,070 But you'll see that this is fairly boilerplate eventually. 278 00:14:42,070 --> 00:14:44,500 I'm not going to run an HTTP server, but I 279 00:14:44,500 --> 00:14:49,030 am going to run flask run, which will turn this app.py into a working web 280 00:14:49,030 --> 00:14:49,707 application. 281 00:14:49,707 --> 00:14:52,540 The output after I hit Enter is going to look a little more cryptic. 282 00:14:52,540 --> 00:14:54,670 It's going to warn me this is a development server. 283 00:14:54,670 --> 00:14:57,110 You should not use that same command in the real world. 284 00:14:57,110 --> 00:14:59,860 You should actually configure Flask a little differently if you're 285 00:14:59,860 --> 00:15:01,277 going to use it in the real world. 286 00:15:01,277 --> 00:15:05,260 But it does show me the random URL that GitHub created for me. 287 00:15:05,260 --> 00:15:07,240 And I'm going to go ahead and open this URL. 288 00:15:07,240 --> 00:15:08,770 It's going to open in a new tab. 289 00:15:08,770 --> 00:15:11,620 And, voila, that is my web application. 290 00:15:11,620 --> 00:15:13,720 Completely underwhelming, but you'll notice 291 00:15:13,720 --> 00:15:15,760 that, even though Chrome is hiding this, this 292 00:15:15,760 --> 00:15:21,170 is the equivalent of my having visited at the end of this URL simply a slash. 293 00:15:21,170 --> 00:15:26,320 All right, if I zoom out here, though, and maybe right-click or Control-click, 294 00:15:26,320 --> 00:15:28,510 and I choose View page source-- 295 00:15:28,510 --> 00:15:30,910 recall, this is available in most every browser-- 296 00:15:30,910 --> 00:15:34,180 you'll see that this isn't actually HTML because, at the moment, 297 00:15:34,180 --> 00:15:36,910 I'm literally just returning, quote, unquote, "hello, world". 298 00:15:36,910 --> 00:15:37,983 So, yes, it's text. 299 00:15:37,983 --> 00:15:39,400 It's being rendered as a web page. 300 00:15:39,400 --> 00:15:42,430 But it's not technically a web page that has valid HTML. 301 00:15:42,430 --> 00:15:44,020 So what I'm going to do here is this. 302 00:15:44,020 --> 00:15:46,180 I'm going to go back into VS Code. 303 00:15:46,180 --> 00:15:48,370 I'm going to open a second terminal by clicking 304 00:15:48,370 --> 00:15:50,650 the plus icon toward the bottom right of my screen, 305 00:15:50,650 --> 00:15:52,570 just so I can keep the server running. 306 00:15:52,570 --> 00:15:54,890 But-- actually, nope, let me go ahead and do this. 307 00:15:54,890 --> 00:15:56,200 Let me kill this terminal. 308 00:15:56,200 --> 00:15:59,210 Let me actually-- oops, I killed the wrong one. 309 00:15:59,210 --> 00:16:03,430 Let me instead go into my hello directory again. 310 00:16:03,430 --> 00:16:06,190 Let me open up app.py. 311 00:16:06,190 --> 00:16:11,020 And this time, instead of saying hello, world, let me do this. 312 00:16:11,020 --> 00:16:14,500 I want to return the contents of index.html, 313 00:16:14,500 --> 00:16:18,710 which is that whole file I created, but I can't just specify its file name. 314 00:16:18,710 --> 00:16:20,030 But I can do this. 315 00:16:20,030 --> 00:16:22,210 I can call a function called render_template, 316 00:16:22,210 --> 00:16:23,470 which comes with Flask. 317 00:16:23,470 --> 00:16:26,590 And its purpose in life is to go get that file from a folder 318 00:16:26,590 --> 00:16:29,080 called templates, open it up and then send 319 00:16:29,080 --> 00:16:31,150 all of those bytes, all of those characters 320 00:16:31,150 --> 00:16:33,318 to the user's actual browser. 321 00:16:33,318 --> 00:16:34,610 So let me go ahead and do this. 322 00:16:34,610 --> 00:16:36,070 Let me open my terminal again. 323 00:16:36,070 --> 00:16:40,210 Let me do flask run inside of this same folder hello. 324 00:16:40,210 --> 00:16:43,960 I'm now going to go back to my tab here, click Reload, 325 00:16:43,960 --> 00:16:46,240 and nothing appears to have changed. 326 00:16:46,240 --> 00:16:50,680 But if I right-click and choose View source this time after reloading, 327 00:16:50,680 --> 00:16:54,460 now you'll see all of the HTML that I composed. 328 00:16:54,460 --> 00:16:57,410 All right, so this has taken way more steps 329 00:16:57,410 --> 00:17:01,290 to do what we achieved by just running HTTP server last week. 330 00:17:01,290 --> 00:17:05,180 But here's where now things can get a little interesting, whereby 331 00:17:05,180 --> 00:17:08,000 now that we have this framework laid out, 332 00:17:08,000 --> 00:17:10,460 we can actually introduce other features of the framework 333 00:17:10,460 --> 00:17:12,592 to make things more dynamic. 334 00:17:12,592 --> 00:17:14,550 So, for instance, what I'm going to do is this. 335 00:17:14,550 --> 00:17:17,270 I'm going to now introduce a feature of that variable 336 00:17:17,270 --> 00:17:21,740 that I also imported called request, which refers to any HTTP request. 337 00:17:21,740 --> 00:17:23,569 And it turns out, there's a property inside 338 00:17:23,569 --> 00:17:25,819 of there called args, which is actually going 339 00:17:25,819 --> 00:17:28,369 to be a dictionary of all of the key value pairs 340 00:17:28,369 --> 00:17:30,950 that the human might've provided via the URL. 341 00:17:30,950 --> 00:17:34,670 So I don't have to figure out, how do I find the thing after the question mark? 342 00:17:34,670 --> 00:17:36,980 I don't have to worry about parsing the ampersands. 343 00:17:36,980 --> 00:17:39,470 Flask does all of that for me and just hands 344 00:17:39,470 --> 00:17:43,830 me anything after the URL in a Python dictionary instead. 345 00:17:43,830 --> 00:17:44,790 So let me do this. 346 00:17:44,790 --> 00:17:47,697 Let me go back to VS Code here. 347 00:17:47,697 --> 00:17:49,280 Let me go ahead and hide the terminal. 348 00:17:49,280 --> 00:17:54,770 But, in app.py, let me go ahead and make a relatively simple change. 349 00:17:54,770 --> 00:17:58,860 Let me go ahead and do this. 350 00:17:58,860 --> 00:18:05,300 Let me go ahead and open up in my hello folder, let me open up index.html. 351 00:18:05,300 --> 00:18:09,260 And let me go ahead and get rid of world and just put a placeholder here. 352 00:18:09,260 --> 00:18:13,020 Using curly brackets, two of them, on the left and right, 353 00:18:13,020 --> 00:18:15,600 I'm going to go ahead and plug in a variable like name. 354 00:18:15,600 --> 00:18:19,430 So here's now where I'm treating index.html not as literally 355 00:18:19,430 --> 00:18:23,730 an HTML page anymore, but more as a template in the literal sense. 356 00:18:23,730 --> 00:18:25,910 So a template is kind of like a blueprint whereby 357 00:18:25,910 --> 00:18:29,000 you can construct most of what you want the human to see but then 358 00:18:29,000 --> 00:18:31,460 leave little placeholders in, a la a blueprint 359 00:18:31,460 --> 00:18:33,440 where you can fill in certain blanks. 360 00:18:33,440 --> 00:18:37,120 The double curly quotes here is actually a feature 361 00:18:37,120 --> 00:18:38,870 of technically another language-- it's not 362 00:18:38,870 --> 00:18:40,670 a programming language-- called Jinja. 363 00:18:40,670 --> 00:18:44,000 But Jinja is simply a language that a bunch of other humans 364 00:18:44,000 --> 00:18:46,460 came up with that standardizes what syntax 365 00:18:46,460 --> 00:18:49,770 you can use for these placeholders and some other features as well. 366 00:18:49,770 --> 00:18:52,550 So this is going to happen more and more as you progress more 367 00:18:52,550 --> 00:18:54,153 in programming and CS. 368 00:18:54,153 --> 00:18:57,320 It's not going to be as simple as, oh, I'm implementing something in Python. 369 00:18:57,320 --> 00:19:00,410 Or, oh, I'm implementing something in C. It's generally going to be, 370 00:19:00,410 --> 00:19:04,350 oh, I'm implementing something with this full stack of software, 371 00:19:04,350 --> 00:19:09,660 including HTML, CSS, Python, some SQL, some Jinja, and so forth. 372 00:19:09,660 --> 00:19:13,220 So into your vocabulary is now going to generally come a list of technologies 373 00:19:13,220 --> 00:19:15,410 that you're using when solving some problem, no 374 00:19:15,410 --> 00:19:17,210 longer individual languages. 375 00:19:17,210 --> 00:19:18,920 So, by this, I just mean this. 376 00:19:18,920 --> 00:19:22,250 The Flask framework took a look around the internet and saw, 377 00:19:22,250 --> 00:19:24,860 OK, the humans at the Jinja group came up 378 00:19:24,860 --> 00:19:27,830 with a nice simple syntax for putting placeholders in. 379 00:19:27,830 --> 00:19:30,800 Let's support that syntax in Flask. 380 00:19:30,800 --> 00:19:33,720 So it's sort of one framework collaborating with another. 381 00:19:33,720 --> 00:19:38,450 So if I go back to app.py now, how do I actually pass from my application 382 00:19:38,450 --> 00:19:41,750 to that template whatever the human has typed in? 383 00:19:41,750 --> 00:19:43,830 Well, it turns out I can go ahead and do this. 384 00:19:43,830 --> 00:19:46,500 Let me go ahead and in my index function, which, again, 385 00:19:46,500 --> 00:19:50,697 is what's going to get called anytime someone visits that slash route, 386 00:19:50,697 --> 00:19:53,030 I'm going to go ahead and create a variable called name. 387 00:19:53,030 --> 00:19:56,790 I'm going to set it equal to requests.args. 388 00:19:56,790 --> 00:20:01,760 And then I'm going to go ahead and say, how about, quote, unquote, "name". 389 00:20:01,760 --> 00:20:05,390 I claimed, a moment ago, that args is a dictionary that 390 00:20:05,390 --> 00:20:08,750 just comes automatically with Flask and whenever 391 00:20:08,750 --> 00:20:11,030 a human makes a request to the server, and it 392 00:20:11,030 --> 00:20:14,330 puts in that dictionary all of the key value pairs from the URL. 393 00:20:14,330 --> 00:20:16,460 And the last thing I'm going to do here is this. 394 00:20:16,460 --> 00:20:18,320 I'm going to actually say-- and, actually, just let 395 00:20:18,320 --> 00:20:19,550 me make this more explicit. 396 00:20:19,550 --> 00:20:22,640 Let me call this placeholder literally placeholder. 397 00:20:22,640 --> 00:20:25,230 And what I'm going to do now is, in render template, 398 00:20:25,230 --> 00:20:28,522 I'm going to take advantage of one other feature that comes with this function. 399 00:20:28,522 --> 00:20:30,780 It can take one or more arguments. 400 00:20:30,780 --> 00:20:33,770 And if you pass in more, you can specify variables 401 00:20:33,770 --> 00:20:35,730 that you want the function to have access to. 402 00:20:35,730 --> 00:20:40,940 So I can literally do something like this, placeholder equals name. 403 00:20:40,940 --> 00:20:44,090 So recall that Python supports named parameters, which 404 00:20:44,090 --> 00:20:47,040 just means that you can pass in multiple arguments to a function. 405 00:20:47,040 --> 00:20:48,870 But you can specify them by name. 406 00:20:48,870 --> 00:20:53,240 So I am calling one of these arguments placeholder. 407 00:20:53,240 --> 00:20:55,490 And I'm setting the value of that argument 408 00:20:55,490 --> 00:20:58,740 equal to name, which itself is a variable that I 409 00:20:58,740 --> 00:21:00,790 defined just a moment ago. 410 00:21:00,790 --> 00:21:02,730 So, now, what's going to happen? 411 00:21:02,730 --> 00:21:08,010 Well, let me actually go back to my VS Code. 412 00:21:08,010 --> 00:21:11,010 I'm going to go ahead and run Flask as I did before. 413 00:21:11,010 --> 00:21:12,970 The URL is not going to change in this case, 414 00:21:12,970 --> 00:21:14,820 I'm going to go back to my other tab. 415 00:21:14,820 --> 00:21:18,900 I'm going to go ahead now and change the URL manually-- let me zoom in here-- 416 00:21:18,900 --> 00:21:20,204 to be /?name=David. 417 00:21:20,204 --> 00:21:22,678 418 00:21:22,678 --> 00:21:23,970 I'm not going to hit Enter yet. 419 00:21:23,970 --> 00:21:24,990 Let me zoom out. 420 00:21:24,990 --> 00:21:31,590 But, when I do zoom out here, I think we should see now, hello, David. 421 00:21:31,590 --> 00:21:32,340 So here we go. 422 00:21:32,340 --> 00:21:33,240 Enter. 423 00:21:33,240 --> 00:21:35,370 And, voila, we see hello, David. 424 00:21:35,370 --> 00:21:37,470 But, more interestingly, if I view source 425 00:21:37,470 --> 00:21:40,560 here by right-clicking or Control-clicking on the page or opening 426 00:21:40,560 --> 00:21:43,320 developer tools and so forth and go to View page source, 427 00:21:43,320 --> 00:21:46,170 it appears that what the server sent to my browser 428 00:21:46,170 --> 00:21:49,050 is literally a web page that says hello, David. 429 00:21:49,050 --> 00:21:50,490 There is no more placeholder. 430 00:21:50,490 --> 00:21:52,410 There is no more curly braces. 431 00:21:52,410 --> 00:21:56,370 Literally, the content that came from the server is that string. 432 00:21:56,370 --> 00:21:58,620 And so this is the distinction now between being 433 00:21:58,620 --> 00:22:00,690 a static application versus dynamic. 434 00:22:00,690 --> 00:22:05,530 What I wrote statically was literally this index.html file. 435 00:22:05,530 --> 00:22:08,130 But what got served dynamically is that file 436 00:22:08,130 --> 00:22:13,870 plus the substitution or interpolation of that placeholder variable. 437 00:22:13,870 --> 00:22:17,340 So we have the beginnings, it would seem, of a web application. 438 00:22:17,340 --> 00:22:18,510 And think back to Google. 439 00:22:18,510 --> 00:22:23,220 Google is essentially implemented the same way, /search?q=cats. 440 00:22:23,220 --> 00:22:26,700 So they're doing more with the key value pair than just spitting it back out, 441 00:22:26,700 --> 00:22:30,750 but we have the beginnings now of a dynamic application. 442 00:22:30,750 --> 00:22:38,330 Any questions on any of this code or this framework thus far? 443 00:22:38,330 --> 00:22:40,668 Any questions thus far. 444 00:22:40,668 --> 00:22:42,460 No, all right, well, let's see what happens 445 00:22:42,460 --> 00:22:44,140 if I don't cooperate like a human. 446 00:22:44,140 --> 00:22:47,470 Let me actually go ahead and get rid of that parameter, hit Enter again, 447 00:22:47,470 --> 00:22:51,200 and I actually now got an HTTP 400 error. 448 00:22:51,200 --> 00:22:52,490 So this actually seems bad. 449 00:22:52,490 --> 00:22:56,110 And it's a little subtle, but if I zoom into the tab here, it indeed says 400. 450 00:22:56,110 --> 00:22:59,500 That's one of the HTTP status codes that you generally shouldn't 451 00:22:59,500 --> 00:23:01,180 see unless something goes wrong. 452 00:23:01,180 --> 00:23:02,470 200 meant OK. 453 00:23:02,470 --> 00:23:04,390 404 meant file not found. 454 00:23:04,390 --> 00:23:07,300 400 means that something went wrong. 455 00:23:07,300 --> 00:23:10,210 I guess I didn't pass in a name as I was supposed to. 456 00:23:10,210 --> 00:23:14,080 But that's only because I was sort of blindly expecting this placeholder 457 00:23:14,080 --> 00:23:14,780 to exist. 458 00:23:14,780 --> 00:23:18,440 So let me be a little smarter and code a little more defensively now as follows. 459 00:23:18,440 --> 00:23:19,400 Let me say this. 460 00:23:19,400 --> 00:23:26,770 How about if there is a name parameter in the requests arguments, 461 00:23:26,770 --> 00:23:29,440 then go ahead and create a variable called name, 462 00:23:29,440 --> 00:23:33,280 set it equal to request.args, quote, unquote, "name". 463 00:23:33,280 --> 00:23:35,740 So treat it as a dictionary with that key. 464 00:23:35,740 --> 00:23:39,160 Else, if there is no name in the URL, let's 465 00:23:39,160 --> 00:23:41,750 just default this variable to being something sensible like, 466 00:23:41,750 --> 00:23:42,770 quote, unquote, "world". 467 00:23:42,770 --> 00:23:46,250 So, now, let's proactively, using some sort of "week one style" 468 00:23:46,250 --> 00:23:48,980 conditional code, albeit in Python now from week six, 469 00:23:48,980 --> 00:23:53,400 let's just check is name in the URL, if so grab its value. 470 00:23:53,400 --> 00:23:55,740 Otherwise, default to world instead. 471 00:23:55,740 --> 00:23:57,740 So I'm still going to leave this code as is, 472 00:23:57,740 --> 00:24:00,920 passing in the placeholder for this template to get plugged in here. 473 00:24:00,920 --> 00:24:06,020 But, now, if I go back to the browser and I just reload without passing 474 00:24:06,020 --> 00:24:10,140 in name=David or anything else, now we have a sensible default. 475 00:24:10,140 --> 00:24:16,460 And if I go back to my View source tab and reload, I'll see that I have hello, 476 00:24:16,460 --> 00:24:17,760 world in this case. 477 00:24:17,760 --> 00:24:23,000 However, if I go back up to that URL and do /?name=David, now we have David. 478 00:24:23,000 --> 00:24:26,090 If I change the URL to be name=Carter, now we have Carter. 479 00:24:26,090 --> 00:24:29,158 So, indeed, we do have the beginnings of something that's more dynamic. 480 00:24:29,158 --> 00:24:30,950 Of course, this is a little tedious to have 481 00:24:30,950 --> 00:24:32,750 to write out this if and this else. 482 00:24:32,750 --> 00:24:36,800 There's ways to condense this code to be a little tighter and a little faster 483 00:24:36,800 --> 00:24:37,970 to actually implement. 484 00:24:37,970 --> 00:24:40,520 And, in fact, let me go ahead and propose that. 485 00:24:40,520 --> 00:24:42,020 We just do this. 486 00:24:42,020 --> 00:24:45,200 Instead of treating args as a dictionary as 487 00:24:45,200 --> 00:24:48,080 we did with square brackets-- which can cause problems 488 00:24:48,080 --> 00:24:50,130 if that key does not exist. 489 00:24:50,130 --> 00:24:52,670 In fact, let me go back to that version. 490 00:24:52,670 --> 00:24:56,720 Let me undo, undo, undo, whereby I'm just blindly 491 00:24:56,720 --> 00:25:00,080 going into request.args to get name. 492 00:25:00,080 --> 00:25:03,200 In fact, instead of just blindly indexing into this dictionary 493 00:25:03,200 --> 00:25:06,210 called args, which turns out we can do this instead. 494 00:25:06,210 --> 00:25:09,560 Let me go ahead and say request.args.get, 495 00:25:09,560 --> 00:25:12,380 which is a function that comes with a dictionary, 496 00:25:12,380 --> 00:25:15,290 and I can specify the name of the key that I want to get. 497 00:25:15,290 --> 00:25:19,100 And, by default, if there is no key called name in a dictionary, 498 00:25:19,100 --> 00:25:21,200 you're going to get back a default value of none, 499 00:25:21,200 --> 00:25:24,020 which is kind of like Python's equivalent of null, 500 00:25:24,020 --> 00:25:26,120 but it's none, capital N, in Python. 501 00:25:26,120 --> 00:25:28,370 But if you want to give it a different default, 502 00:25:28,370 --> 00:25:30,293 it's handy to know that this get function, 503 00:25:30,293 --> 00:25:32,210 which you can use with dictionaries in general 504 00:25:32,210 --> 00:25:34,940 can take a second argument, which will be the default 505 00:25:34,940 --> 00:25:40,560 value that you do get back if, in fact, there is no such key called name. 506 00:25:40,560 --> 00:25:43,680 So what this means is I can actually now keep all of the code the same, 507 00:25:43,680 --> 00:25:44,763 but it's a little tighter. 508 00:25:44,763 --> 00:25:46,070 There's no if or else. 509 00:25:46,070 --> 00:25:49,760 I can go back to my other browser window, click reload, 510 00:25:49,760 --> 00:25:51,200 and it still works for Carter. 511 00:25:51,200 --> 00:25:54,380 But if I get rid of that, I can now have hello, 512 00:25:54,380 --> 00:25:56,420 world still working just as before. 513 00:25:56,420 --> 00:25:59,870 And just to add one more potential point of confusion to the mix, 514 00:25:59,870 --> 00:26:05,990 it's a little dopey to say placeholder literally in your template, 515 00:26:05,990 --> 00:26:10,070 especially if you're going to be using multiple pairs of curly braces for one 516 00:26:10,070 --> 00:26:11,670 variable or another or another. 517 00:26:11,670 --> 00:26:13,910 So better style would be to actually call 518 00:26:13,910 --> 00:26:17,760 the variable what makes sense for what it is you're plugging in. 519 00:26:17,760 --> 00:26:20,540 So hello, name, with the name in curly braces. 520 00:26:20,540 --> 00:26:24,080 This is-- I show this, though, because it gets a little confusing 521 00:26:24,080 --> 00:26:27,493 if your variable's called name, and that's still 522 00:26:27,493 --> 00:26:28,910 going to be the value you pass in. 523 00:26:28,910 --> 00:26:31,400 You're going to very often see in the world of Flask 524 00:26:31,400 --> 00:26:35,540 this convention, where you literally write something equals something where 525 00:26:35,540 --> 00:26:37,580 the names there are exactly the same. 526 00:26:37,580 --> 00:26:41,190 And the only thing to keep in mind here is that the name-- 527 00:26:41,190 --> 00:26:43,910 this is the name of this parameter. 528 00:26:43,910 --> 00:26:46,670 This is the value of this parameter. 529 00:26:46,670 --> 00:26:50,210 The fact that everything seems to be called name in this program 530 00:26:50,210 --> 00:26:53,630 is because these technical terms are colliding with the English word 531 00:26:53,630 --> 00:26:57,420 that you and I know as name for my name, Carter's name, and so forth. 532 00:26:57,420 --> 00:27:01,700 But get past that only because it will be very common to literally see 533 00:27:01,700 --> 00:27:04,640 something equals something, where just for visual convenience 534 00:27:04,640 --> 00:27:08,510 the variable's name is exactly the value that you want to pass in. 535 00:27:08,510 --> 00:27:11,720 So that too here is conventional. 536 00:27:11,720 --> 00:27:13,220 All right, a little cryptic. 537 00:27:13,220 --> 00:27:17,270 But common boilerplate that you'll start to see again and again. 538 00:27:17,270 --> 00:27:19,490 Any questions now about this? 539 00:27:19,490 --> 00:27:22,530 540 00:27:22,530 --> 00:27:23,220 No? 541 00:27:23,220 --> 00:27:26,400 OK, so let's make things a little more interesting and representative 542 00:27:26,400 --> 00:27:27,900 of a real-world app. 543 00:27:27,900 --> 00:27:31,470 Let me propose that, now, we go about implementing 544 00:27:31,470 --> 00:27:34,578 maybe a second template altogether. 545 00:27:34,578 --> 00:27:36,120 In fact, let me go ahead and do this. 546 00:27:36,120 --> 00:27:39,750 It'd be nice if the human doesn't need to be technologically savvy enough 547 00:27:39,750 --> 00:27:42,420 to say that, oh, if you want to be greeted by this website, 548 00:27:42,420 --> 00:27:45,840 you have to literally change the URL yourself, type in name equals-- 549 00:27:45,840 --> 00:27:46,560 no one does that. 550 00:27:46,560 --> 00:27:49,620 That's just not how the web works in terms of user interface, 551 00:27:49,620 --> 00:27:52,050 but that is how browsers and servers do work. 552 00:27:52,050 --> 00:27:54,488 But, of course, on the web, we almost always use forms. 553 00:27:54,488 --> 00:27:55,780 So let me go ahead and do this. 554 00:27:55,780 --> 00:28:00,570 Let me go into index.html, and let me get rid of just this hello, body. 555 00:28:00,570 --> 00:28:03,760 And, instead, let me actually create an HTML form. 556 00:28:03,760 --> 00:28:08,460 This form is going to use the get method if only so that we can 557 00:28:08,460 --> 00:28:10,830 see what's going on inside of the URL. 558 00:28:10,830 --> 00:28:15,300 This form is going to have an input where I'm going to turn autocomplete 559 00:28:15,300 --> 00:28:17,010 off, just like last week. 560 00:28:17,010 --> 00:28:20,880 I'm going to do auto focus just to move the cursor there nicely by default. 561 00:28:20,880 --> 00:28:25,840 Somewhat confusingly, I'm going to give this input a name of name 562 00:28:25,840 --> 00:28:29,540 because I want Carter's name, my name, or someone else's human name. 563 00:28:29,540 --> 00:28:31,780 But I'm going to give it placeholder text of, 564 00:28:31,780 --> 00:28:33,670 quote, unquote, "Name", capitalized, just 565 00:28:33,670 --> 00:28:37,030 to be grammatically clear as to what we're prompting the user for. 566 00:28:37,030 --> 00:28:39,775 And the type of this field is going to be text. 567 00:28:39,775 --> 00:28:42,400 Although, that's the implied default if I don't give it a type. 568 00:28:42,400 --> 00:28:45,790 I'm going to lastly have a button in this form, the type of which 569 00:28:45,790 --> 00:28:49,000 is submit so that the browser knows to submit this form. 570 00:28:49,000 --> 00:28:51,370 And the label I'm going to put on this button is Greet. 571 00:28:51,370 --> 00:28:53,245 So I'm going to type in my name, click Greet. 572 00:28:53,245 --> 00:28:55,610 And I want to see hello, David or the like. 573 00:28:55,610 --> 00:28:58,930 But I need to specify the action for this form. 574 00:28:58,930 --> 00:29:02,120 And recall that, when we implemented "Google", quote, unquote, 575 00:29:02,120 --> 00:29:03,620 we did a little something like this. 576 00:29:03,620 --> 00:29:08,080 Https://www.google.com/search, well, we're 577 00:29:08,080 --> 00:29:09,530 not going to punt to Google today. 578 00:29:09,530 --> 00:29:12,200 We are implementing our own applications. 579 00:29:12,200 --> 00:29:16,090 So if this were a search application, I could literally have an action 580 00:29:16,090 --> 00:29:19,750 of /search, but let's do something a little more semantically sensible. 581 00:29:19,750 --> 00:29:22,810 Let's create our brand new route called /greet. 582 00:29:22,810 --> 00:29:24,290 This is not the name of a folder. 583 00:29:24,290 --> 00:29:25,457 It's not the name of a file. 584 00:29:25,457 --> 00:29:30,040 It's more generically a path or a route that is now up to me to implement. 585 00:29:30,040 --> 00:29:33,580 If I go back, though, to this application and reload this page, 586 00:29:33,580 --> 00:29:36,580 notice that I have the beginnings of a more user-friendly form 587 00:29:36,580 --> 00:29:38,420 that's asking me for this name. 588 00:29:38,420 --> 00:29:41,560 However, if I do type in David and click Greet-- 589 00:29:41,560 --> 00:29:43,880 I'll zoom in just a moment-- 590 00:29:43,880 --> 00:29:50,150 notice that the URL does change to /greet?name=David just like google.com 591 00:29:50,150 --> 00:29:50,650 works. 592 00:29:50,650 --> 00:29:53,560 But, of course, we're getting a 404 not found because I 593 00:29:53,560 --> 00:29:55,570 haven't implemented this route yet. 594 00:29:55,570 --> 00:29:56,630 So let me zoom out. 595 00:29:56,630 --> 00:29:58,270 Let me go back to VS Code. 596 00:29:58,270 --> 00:30:03,610 Let me open app.py and make a little bit of a change there. 597 00:30:03,610 --> 00:30:08,530 Let me make a little bit of a change and say this in app.py. 598 00:30:08,530 --> 00:30:14,590 In app.py, instead of just getting the user's name and this default, 599 00:30:14,590 --> 00:30:18,970 let's simplify the index route and just have it sole purpose in life 600 00:30:18,970 --> 00:30:23,020 be to render index.html, the thing that contains the form. 601 00:30:23,020 --> 00:30:28,090 I'm going to now, though, create a second route, so app.route, quote, 602 00:30:28,090 --> 00:30:29,410 unquote, "/greet". 603 00:30:29,410 --> 00:30:31,420 And I could call this route anything I want. 604 00:30:31,420 --> 00:30:32,950 But greet seems sensible. 605 00:30:32,950 --> 00:30:35,380 I'm going to call the function below it anything I want. 606 00:30:35,380 --> 00:30:38,672 But, just to keep myself sane, I'm going to call it the same thing as the route 607 00:30:38,672 --> 00:30:40,640 even though that's not strictly required. 608 00:30:40,640 --> 00:30:44,240 And then, in this route, I'm going to go ahead and do this. 609 00:30:44,240 --> 00:30:48,760 I'm going to create a variable called name, set it equal to request.args.get, 610 00:30:48,760 --> 00:30:52,570 quote, unquote, "name", then, quote, unquote, "world", so 611 00:30:52,570 --> 00:30:54,250 the exact same line as before. 612 00:30:54,250 --> 00:30:59,200 And then I'm going to return render_template, which 613 00:30:59,200 --> 00:31:00,970 is the function that comes with Flask. 614 00:31:00,970 --> 00:31:05,740 I'm going to specify this time, though, render a template called greet.html-- 615 00:31:05,740 --> 00:31:08,980 which doesn't exist yet, but that's not going to be a hard problem to solve-- 616 00:31:08,980 --> 00:31:12,410 and pass in that variable. 617 00:31:12,410 --> 00:31:15,160 So the last thing I need to do is this. 618 00:31:15,160 --> 00:31:17,950 And I'm going to cheat and copy and, in a moment, 619 00:31:17,950 --> 00:31:22,820 paste the contents of index.html into a new file called greet.html as follows. 620 00:31:22,820 --> 00:31:25,300 Let me open up VS Code here in my other terminal. 621 00:31:25,300 --> 00:31:31,750 Let me go ahead and write code templates/greet.html. 622 00:31:31,750 --> 00:31:35,530 Notice that I'm making sure to put the new file in the templates folder. 623 00:31:35,530 --> 00:31:38,620 Or I could cd into it and then run the code command. 624 00:31:38,620 --> 00:31:40,202 That's going to give me a new file. 625 00:31:40,202 --> 00:31:42,160 I'm going to hide my terminal, paste that code, 626 00:31:42,160 --> 00:31:43,660 and I'm going to get rid of the form. 627 00:31:43,660 --> 00:31:46,202 And, frankly, I should've just copied and pasted this earlier 628 00:31:46,202 --> 00:31:49,900 because the only thing I'm going to put in greet.html is hello 629 00:31:49,900 --> 00:31:52,600 and then, in curly braces, my placeholder, which 630 00:31:52,600 --> 00:31:54,950 we started to call name a moment ago. 631 00:31:54,950 --> 00:31:59,530 So, to recap if I go into my terminal again, if I type ls, 632 00:31:59,530 --> 00:32:00,940 I've still got app.py. 633 00:32:00,940 --> 00:32:02,180 I've still got templates. 634 00:32:02,180 --> 00:32:06,340 But if I look inside of templates now, I've got two templates-- index.html 635 00:32:06,340 --> 00:32:10,510 and greet.html. index.html is the thing you see when you visit my website. 636 00:32:10,510 --> 00:32:15,520 greet.html is the thing you see when you submit that form, it would seem. 637 00:32:15,520 --> 00:32:19,120 So, indeed, if I go back to my browser and hit back-- 638 00:32:19,120 --> 00:32:20,560 so I get back to that form. 639 00:32:20,560 --> 00:32:22,070 For good measure, I'm going to reload because I 640 00:32:22,070 --> 00:32:24,560 want to make sure I have the latest version of everything. 641 00:32:24,560 --> 00:32:26,780 I'm going to now try typing my name David. 642 00:32:26,780 --> 00:32:27,720 I'll zoom in. 643 00:32:27,720 --> 00:32:31,820 You'll see that the URL will again change to /greet with the question mark 644 00:32:31,820 --> 00:32:32,660 and my name. 645 00:32:32,660 --> 00:32:36,800 But, hopefully, we now indeed don't see a 404 646 00:32:36,800 --> 00:32:39,420 because the new route actually exists. 647 00:32:39,420 --> 00:32:41,990 And if I zoom out, right-click or Control-click 648 00:32:41,990 --> 00:32:44,390 and go to View page source, you still see 649 00:32:44,390 --> 00:32:49,100 what appears to be a HTML file just for me even though it was dynamically 650 00:32:49,100 --> 00:32:52,280 generated instead. 651 00:32:52,280 --> 00:32:56,450 All right, if you're on board that this seems to be a correct application 652 00:32:56,450 --> 00:32:59,750 insofar as it does what I wanted it to do, 653 00:32:59,750 --> 00:33:03,410 let's critique the design as we've been in the habit of doing. 654 00:33:03,410 --> 00:33:10,390 I've now got three files, app.py, greet.html, and index.html. 655 00:33:10,390 --> 00:33:13,930 What might you not like about the design of this web application 656 00:33:13,930 --> 00:33:16,210 even if you've never done this stuff before? 657 00:33:16,210 --> 00:33:17,108 Yeah. 658 00:33:17,108 --> 00:33:18,900 AUDIENCE: [INAUDIBLE] 659 00:33:18,900 --> 00:33:20,250 660 00:33:20,250 --> 00:33:23,490 DAVID J. MALAN: Yeah, greet and index.html have the same contents, 661 00:33:23,490 --> 00:33:26,827 except for that one or few lines in the middle of the body. 662 00:33:26,827 --> 00:33:29,160 I mean, I literally copied and pasted, which, generally, 663 00:33:29,160 --> 00:33:31,327 even though I do it sometimes in class to save time, 664 00:33:31,327 --> 00:33:34,410 if I end up with the same code in my files, 665 00:33:34,410 --> 00:33:37,440 that's probably cutting some corner and not the best design. 666 00:33:37,440 --> 00:33:37,943 Why? 667 00:33:37,943 --> 00:33:40,860 Well, what if I want to go in and change the title of this application 668 00:33:40,860 --> 00:33:42,625 from hello to something else. 669 00:33:42,625 --> 00:33:43,500 It's not a huge deal. 670 00:33:43,500 --> 00:33:45,083 But I have to change it in two places. 671 00:33:45,083 --> 00:33:47,160 What if I've got some pretty CSS that I've added? 672 00:33:47,160 --> 00:33:49,380 And so I've got some CSS up here in one file. 673 00:33:49,380 --> 00:33:52,140 I need to copy it into another file, change it both places. 674 00:33:52,140 --> 00:33:55,630 Generally, having duplication of anything is bad design. 675 00:33:55,630 --> 00:33:58,770 So it turns out, there is a way to solve this. 676 00:33:58,770 --> 00:34:01,230 And this is one of the features that you really 677 00:34:01,230 --> 00:34:04,890 start to get from a web framework, be it Flask or anything else. 678 00:34:04,890 --> 00:34:07,469 You get solutions to these problems. 679 00:34:07,469 --> 00:34:08,969 So what I'm going to do now is this. 680 00:34:08,969 --> 00:34:12,179 I'm going to create a third and final file for this application 681 00:34:12,179 --> 00:34:15,510 by starting by copying what I have already. 682 00:34:15,510 --> 00:34:19,290 Let me go back to my terminal window here. 683 00:34:19,290 --> 00:34:23,650 And let me create a third template in the templates folder called 684 00:34:23,650 --> 00:34:24,858 layout.html. 685 00:34:24,858 --> 00:34:27,400 It doesn't have to be called that, but that's the convention. 686 00:34:27,400 --> 00:34:28,909 So I'll always do that here. 687 00:34:28,909 --> 00:34:31,239 And, when I create this file and hide my terminal, 688 00:34:31,239 --> 00:34:34,010 I'm going to go ahead and copy-paste all of that content. 689 00:34:34,010 --> 00:34:39,610 But I'm going to go inside of the body of this file, called layout.html, 690 00:34:39,610 --> 00:34:44,469 which is otherwise identical across all of my files. 691 00:34:44,469 --> 00:34:46,900 And what I'm going to do is use slightly weird syntax. 692 00:34:46,900 --> 00:34:50,230 This is more of that Jinja syntax that some other humans came up 693 00:34:50,230 --> 00:34:51,130 with years ago. 694 00:34:51,130 --> 00:34:52,690 And I'm going to do this. 695 00:34:52,690 --> 00:34:55,449 Single curly brace and a percent sign. 696 00:34:55,449 --> 00:35:00,332 I'm going to specify the word block and then any name I want for this block. 697 00:35:00,332 --> 00:35:02,290 And, by convention, I'm going to keep it simple 698 00:35:02,290 --> 00:35:04,810 and just use the exact same thing as the name of the tag 699 00:35:04,810 --> 00:35:06,760 that I'm inside, so block body. 700 00:35:06,760 --> 00:35:10,570 And then I'm going to put a percent sign just before the close curly brace. 701 00:35:10,570 --> 00:35:12,200 I don't need anything inside of this. 702 00:35:12,200 --> 00:35:13,930 So this too's going to look weird at first glance. 703 00:35:13,930 --> 00:35:14,975 But it's a convention. 704 00:35:14,975 --> 00:35:17,350 I'm going to do another curly brace with the percent sign 705 00:35:17,350 --> 00:35:20,710 and then literally the word endblock, no space. 706 00:35:20,710 --> 00:35:24,160 And I'm going to open and close what isn't an HTML tag. 707 00:35:24,160 --> 00:35:26,240 It's a Jinja tag, if you will. 708 00:35:26,240 --> 00:35:28,900 So, again, you see yet more evidence of reasonable humans 709 00:35:28,900 --> 00:35:30,760 in the world kind of disagreeing on syntax 710 00:35:30,760 --> 00:35:33,530 or at least using syntax that's similar in spirit 711 00:35:33,530 --> 00:35:38,530 but doesn't clash with the syntax, the angled brackets that HTML already uses. 712 00:35:38,530 --> 00:35:41,500 Long story short, this is a way of specifying 713 00:35:41,500 --> 00:35:45,460 that you want a placeholder, not just for a single variable's value 714 00:35:45,460 --> 00:35:47,420 but for a whole block of code. 715 00:35:47,420 --> 00:35:48,700 Maybe it's simply a sentence. 716 00:35:48,700 --> 00:35:51,760 Maybe it's a whole-- a web form element or more. 717 00:35:51,760 --> 00:35:54,940 This is a placeholder now for a block of code. 718 00:35:54,940 --> 00:36:00,410 And the way I can do this-- or the way I can use this template-- 719 00:36:00,410 --> 00:36:03,640 and this is where template now is getting all the more literal 720 00:36:03,640 --> 00:36:05,410 in the sense of what templates do. 721 00:36:05,410 --> 00:36:07,130 I'm going to go ahead and do this. 722 00:36:07,130 --> 00:36:11,590 I'm going to go into my two other files, like greet.html. 723 00:36:11,590 --> 00:36:16,120 The only line that's different in this file vis-a-vis index.html 724 00:36:16,120 --> 00:36:18,070 is which line number? 725 00:36:18,070 --> 00:36:20,857 9 is the only line that is unique. 726 00:36:20,857 --> 00:36:22,190 So what I'm going to do is this. 727 00:36:22,190 --> 00:36:24,040 I'm going to highlight that and copy it. 728 00:36:24,040 --> 00:36:26,530 And then I'm going to delete everything else in this file 729 00:36:26,530 --> 00:36:27,850 because it's just redundant. 730 00:36:27,850 --> 00:36:30,130 Everything I need is in layout.html. 731 00:36:30,130 --> 00:36:34,030 At the top of this file, I'm going to use another curly brace and a percent 732 00:36:34,030 --> 00:36:36,160 sign, but I'm going to use a special keyword that 733 00:36:36,160 --> 00:36:38,470 comes with Jinja called extends. 734 00:36:38,470 --> 00:36:42,070 And I'm going to specify in quotes here the name of the template 735 00:36:42,070 --> 00:36:45,320 that I want to extend, so to speak. 736 00:36:45,320 --> 00:36:48,520 So this is an example of what's in computer science known as inheritance. 737 00:36:48,520 --> 00:36:53,800 I want to take everything from that layout and inherit from it 738 00:36:53,800 --> 00:36:56,860 all of its lines but plug in some of my own, 739 00:36:56,860 --> 00:36:59,350 sort of from a parent-child relationship. 740 00:36:59,350 --> 00:37:02,320 Inside of this file, now, I'm going to specify 741 00:37:02,320 --> 00:37:08,590 that the custom body that I want is this block body just like before. 742 00:37:08,590 --> 00:37:11,770 And then, down here, I'm going to preemptively say endblock just 743 00:37:11,770 --> 00:37:13,750 to finish my thought in advance. 744 00:37:13,750 --> 00:37:18,340 And then, inside of this block body, I'm going to simply paste that line of code 745 00:37:18,340 --> 00:37:20,350 that I Stole from the original version. 746 00:37:20,350 --> 00:37:23,230 So I'll concede that this is pretty ugly. 747 00:37:23,230 --> 00:37:26,770 I've added three cryptic-looking lines, all of which 748 00:37:26,770 --> 00:37:29,530 relate to Jinja templating, again, syntax 749 00:37:29,530 --> 00:37:32,770 that humans invented to give you the ability to write templates, 750 00:37:32,770 --> 00:37:33,730 or blueprints. 751 00:37:33,730 --> 00:37:37,480 But the point is that this single line, now line 5, 752 00:37:37,480 --> 00:37:41,290 is going to get plugged into that template, called layout.html, 753 00:37:41,290 --> 00:37:45,230 wherever that body block is meant to go. 754 00:37:45,230 --> 00:37:47,230 Lastly, I'm going to go ahead and do this. 755 00:37:47,230 --> 00:37:53,022 The only lines in index.html that are unique are these here, 9, 10, 11, 12. 756 00:37:53,022 --> 00:37:55,480 So I'm going to highlight those and delete everything else. 757 00:37:55,480 --> 00:37:59,890 And then I'm going to do the exact same thing, extends layout.html at the top 758 00:37:59,890 --> 00:38:03,730 of this file, then, below that, block body. 759 00:38:03,730 --> 00:38:07,420 Inside of the block body, I'm going to then say endblock at the end. 760 00:38:07,420 --> 00:38:10,870 And, in the middle of that, I'm going to paste those lines of code. 761 00:38:10,870 --> 00:38:14,350 Just stylistically, I'm going to indent them just so I'm super clear visually 762 00:38:14,350 --> 00:38:16,060 on what is inside of what. 763 00:38:16,060 --> 00:38:17,290 But that's it. 764 00:38:17,290 --> 00:38:18,310 So ugly? 765 00:38:18,310 --> 00:38:21,510 Yes, but, as soon as your web pages get longer and longer, 766 00:38:21,510 --> 00:38:23,500 this ends up being a drop in the bucket. 767 00:38:23,500 --> 00:38:27,090 It's three ugly-looking lines relative to a lot of HTML 768 00:38:27,090 --> 00:38:30,100 that you might be plugging in and customizing for your application. 769 00:38:30,100 --> 00:38:32,340 So now index.html looks like this. 770 00:38:32,340 --> 00:38:34,390 greet.html looks like this. 771 00:38:34,390 --> 00:38:38,910 And the only way they differ is the actual contents of that block of code. 772 00:38:38,910 --> 00:38:42,900 layout.html is the main blueprint that's going to govern 773 00:38:42,900 --> 00:38:44,760 what the whole website looks like. 774 00:38:44,760 --> 00:38:46,410 I can change the title in one place. 775 00:38:46,410 --> 00:38:49,020 I can add some pretty CSS in one place and so forth. 776 00:38:49,020 --> 00:38:51,090 It's going to apply to each of those files. 777 00:38:51,090 --> 00:38:52,980 And, now, somewhat underwhelmingly perhaps, 778 00:38:52,980 --> 00:38:57,570 if I go back to this application and I click reload, 779 00:38:57,570 --> 00:39:00,970 nothing is different because it still just works. 780 00:39:00,970 --> 00:39:03,840 But I've made arguably a better design because now, 781 00:39:03,840 --> 00:39:07,290 when I change things to Carter here or I get rid of it altogether 782 00:39:07,290 --> 00:39:09,120 and just visit the default-- 783 00:39:09,120 --> 00:39:12,510 rather, if I just visit slash there, I'll get the form. 784 00:39:12,510 --> 00:39:15,390 I've at least handled the situation where-- 785 00:39:15,390 --> 00:39:18,060 I've eliminated the situation where I've just copied and pasted 786 00:39:18,060 --> 00:39:20,570 the same boilerplate code. 787 00:39:20,570 --> 00:39:24,130 So odds are someone like Google is doing something like this. 788 00:39:24,130 --> 00:39:27,100 It's probably fancier certainly than this example. 789 00:39:27,100 --> 00:39:30,670 But any time you search for something on Google, generally, the top of the page 790 00:39:30,670 --> 00:39:31,337 looks the same. 791 00:39:31,337 --> 00:39:33,170 Maybe the bottom of the page looks the same. 792 00:39:33,170 --> 00:39:34,930 There's maybe some ads always at the top. 793 00:39:34,930 --> 00:39:36,710 And then there's 10 search results. 794 00:39:36,710 --> 00:39:40,420 So, probably, what they've done is they have some template that looks roughly 795 00:39:40,420 --> 00:39:42,730 like this with all of the boilerplate stuff 796 00:39:42,730 --> 00:39:45,640 that they want every human to see on every page of search results. 797 00:39:45,640 --> 00:39:50,080 And then they're just somehow customizing the block-- 798 00:39:50,080 --> 00:39:53,680 a block of code somewhere there in the middle. 799 00:39:53,680 --> 00:40:01,040 All right, questions on any of this actual templating technique? 800 00:40:01,040 --> 00:40:02,690 Anything at all? 801 00:40:02,690 --> 00:40:05,000 All right, how about another question about design? 802 00:40:05,000 --> 00:40:08,690 If I go back to this URL here and I search for something like David, 803 00:40:08,690 --> 00:40:12,320 it's not that big a deal that it ends up in the URL. 804 00:40:12,320 --> 00:40:15,890 And, in fact, what's nice about HTTP parameters ending up in the URL 805 00:40:15,890 --> 00:40:18,260 is that URLs are therefore stateful. 806 00:40:18,260 --> 00:40:21,560 If you copy this URL and paste it into an email, 807 00:40:21,560 --> 00:40:25,770 assuming the web server is still up and running at that URL, it will just work. 808 00:40:25,770 --> 00:40:27,530 And the human to whom you send that link, 809 00:40:27,530 --> 00:40:30,020 they will see David or Carter or whatever name's 810 00:40:30,020 --> 00:40:32,570 actually in that form, which may be as useful behavior. 811 00:40:32,570 --> 00:40:34,827 Not so much for this application, but imagine now 812 00:40:34,827 --> 00:40:37,410 that you want to send someone a link of Google search results. 813 00:40:37,410 --> 00:40:42,020 It's a good thing that Google puts q=cats or dogs or birds or whatever 814 00:40:42,020 --> 00:40:45,260 in the URL because then the URL itself is stateful. 815 00:40:45,260 --> 00:40:47,750 What you see is what the recipient will see 816 00:40:47,750 --> 00:40:52,070 because all of the inputs of the server that's requisite is in that URL. 817 00:40:52,070 --> 00:40:54,650 But suppose that this form field, if I go back, 818 00:40:54,650 --> 00:41:01,890 wasn't asking for my name but my credit card number or my password up here. 819 00:41:01,890 --> 00:41:04,170 That should start to rub you the wrong way because it 820 00:41:04,170 --> 00:41:08,190 feels like no good will come from exposing private information in the URL 821 00:41:08,190 --> 00:41:10,710 because if you have a nosy sibling look over your shoulder. 822 00:41:10,710 --> 00:41:11,820 There it is in your search history. 823 00:41:11,820 --> 00:41:14,790 A roommate goes through your autocomplete and finds the data there. 824 00:41:14,790 --> 00:41:17,370 Or if you do, for whatever reason, copy-paste it, 825 00:41:17,370 --> 00:41:21,270 you're accidentally including private information in these URLs. 826 00:41:21,270 --> 00:41:25,110 So I said last week that there is an alternative to sending things 827 00:41:25,110 --> 00:41:28,290 in the URL and that alternative is to use something 828 00:41:28,290 --> 00:41:31,500 that's not called get but a verb in the world of HTTP 829 00:41:31,500 --> 00:41:33,760 that's called post instead. 830 00:41:33,760 --> 00:41:35,850 And it's actually a relatively simple change. 831 00:41:35,850 --> 00:41:42,420 If I go into index.html, I can simply change the method from get to post. 832 00:41:42,420 --> 00:41:44,700 Get is the default. Post is an alternative. 833 00:41:44,700 --> 00:41:47,970 Even though, in some contexts, you'll see capitals, in HTML, 834 00:41:47,970 --> 00:41:51,690 it should be lowercase, another example of left hand not talking to right. 835 00:41:51,690 --> 00:41:56,460 But, in this case, if I go now to my other tab with the browser, 836 00:41:56,460 --> 00:41:59,700 reload the page because I want to get the latest version of the form, 837 00:41:59,700 --> 00:42:01,380 if I now type David-- 838 00:42:01,380 --> 00:42:06,000 and I'll zoom in-- before hitting Enter, if you watch the URL now, 839 00:42:06,000 --> 00:42:12,030 you should not see that ?name=David is up there, 840 00:42:12,030 --> 00:42:14,250 nor would be your credit card or your password. 841 00:42:14,250 --> 00:42:16,650 Unfortunately, we're seeing another HTTP status 842 00:42:16,650 --> 00:42:20,130 code we haven't seen yet, 405, Method Not Allowed. 843 00:42:20,130 --> 00:42:21,160 Well, why is that? 844 00:42:21,160 --> 00:42:23,880 That's because now that I fully control the web server, 845 00:42:23,880 --> 00:42:27,060 I need to tell the web server that I do want to support not just get 846 00:42:27,060 --> 00:42:29,520 which is the default but post as well. 847 00:42:29,520 --> 00:42:31,815 The method the user is using is not supported. 848 00:42:31,815 --> 00:42:33,690 So this is an easy fix even though it's going 849 00:42:33,690 --> 00:42:35,280 to look a little cryptic at first. 850 00:42:35,280 --> 00:42:40,530 If you want your greet method to support not just get but post, 851 00:42:40,530 --> 00:42:43,990 you can specify another argument to this route function. 852 00:42:43,990 --> 00:42:49,050 So the default is literally this, methods= and then in square brackets, 853 00:42:49,050 --> 00:42:50,640 quote, unquote, "GET". 854 00:42:50,640 --> 00:42:51,690 So what is this? 855 00:42:51,690 --> 00:42:56,490 Methods is apparently a named argument being passed into the route function. 856 00:42:56,490 --> 00:42:58,620 I claim its default value is this. 857 00:42:58,620 --> 00:43:01,500 What do the square brackets indicate in Python? 858 00:43:01,500 --> 00:43:02,220 Not a dictionary. 859 00:43:02,220 --> 00:43:03,860 Square brackets. 860 00:43:03,860 --> 00:43:07,700 A list, so it's a list of strings or strs in this case. 861 00:43:07,700 --> 00:43:09,288 This is the implicit default. 862 00:43:09,288 --> 00:43:10,580 So you don't have to type this. 863 00:43:10,580 --> 00:43:13,560 It's just what works out of the box automatically. 864 00:43:13,560 --> 00:43:16,250 But if you want to change this from get to post, 865 00:43:16,250 --> 00:43:20,150 you have to include methods equals a list of the methods 866 00:43:20,150 --> 00:43:21,650 that you do want to support. 867 00:43:21,650 --> 00:43:24,350 For another time, there's other HTTP methods. 868 00:43:24,350 --> 00:43:25,280 There's delete. 869 00:43:25,280 --> 00:43:27,452 There's put. 870 00:43:27,452 --> 00:43:29,660 Those are the two biggies that you might use as well. 871 00:43:29,660 --> 00:43:32,930 Those are generally not supported as easily in the world of browsers, 872 00:43:32,930 --> 00:43:34,910 but get and post certainly are. 873 00:43:34,910 --> 00:43:37,340 If you wanted to support both for whatever reason, 874 00:43:37,340 --> 00:43:40,460 you can literally have a comma separated list of those methods instead. 875 00:43:40,460 --> 00:43:42,770 But we don't really need both for privacy's sake. 876 00:43:42,770 --> 00:43:44,720 I claim I'm only going to use post now. 877 00:43:44,720 --> 00:43:47,930 So now if I go back to my other tab, go back 878 00:43:47,930 --> 00:43:51,480 to the form, reload to make sure everything is as expected, 879 00:43:51,480 --> 00:43:57,980 and now type in David and zoom in, you won't see my name in the URL. 880 00:43:57,980 --> 00:44:02,060 But you will-- or you won't see it-- oh, good, not intended. 881 00:44:02,060 --> 00:44:04,840 But nor will you see it even in the body of the web page. 882 00:44:04,840 --> 00:44:06,490 So it's super secure. 883 00:44:06,490 --> 00:44:09,250 Why? 884 00:44:09,250 --> 00:44:12,720 I screwed up, but why? 885 00:44:12,720 --> 00:44:13,740 Yes. 886 00:44:13,740 --> 00:44:15,570 AUDIENCE: [INAUDIBLE] 887 00:44:15,570 --> 00:44:17,770 888 00:44:17,770 --> 00:44:19,660 DAVID J. MALAN: Yes, so good intuition. 889 00:44:19,660 --> 00:44:22,960 Even if you knew that before, you might think through rationally, 890 00:44:22,960 --> 00:44:24,460 how might this be-- 891 00:44:24,460 --> 00:44:26,180 why might this be behaving this way? 892 00:44:26,180 --> 00:44:32,770 Well, if I go into app.py, it seems that if world is the value of the name 893 00:44:32,770 --> 00:44:36,670 placeholder, well, it must be the case that there is no name 894 00:44:36,670 --> 00:44:39,980 key in request.args in this case. 895 00:44:39,980 --> 00:44:42,520 However, there's an alternative to request.args, 896 00:44:42,520 --> 00:44:44,320 and it's called request.form. 897 00:44:44,320 --> 00:44:48,880 This is another example of visible and hidden being opposites of one another, 898 00:44:48,880 --> 00:44:51,550 request.args and request.form, at least for me, 899 00:44:51,550 --> 00:44:54,380 are not obvious mappings to GET and POST, respectively. 900 00:44:54,380 --> 00:44:56,270 But that's what the Flask folks did. 901 00:44:56,270 --> 00:44:59,770 And so the simple fix now, if I go back to VS Code, 902 00:44:59,770 --> 00:45:04,030 is to change request.args to request.form 903 00:45:04,030 --> 00:45:07,750 if you want to use post instead of get. 904 00:45:07,750 --> 00:45:12,440 This is a weird misnomer because they're both coming from forms, 905 00:45:12,440 --> 00:45:14,410 whether you're using GET or POST. 906 00:45:14,410 --> 00:45:16,930 But this is what some folks decided. 907 00:45:16,930 --> 00:45:20,990 So let me go back to my browser, go back to the original form, reload 908 00:45:20,990 --> 00:45:24,530 to make sure I get the fresh HTML, type in my name now, David, 909 00:45:24,530 --> 00:45:26,510 zoom in, and click Greet. 910 00:45:26,510 --> 00:45:29,600 And, this time, you won't see my name in the URL, 911 00:45:29,600 --> 00:45:33,000 but you should see it in the body of the page. 912 00:45:33,000 --> 00:45:35,300 So we've achieved some form of privacy, if you will. 913 00:45:35,300 --> 00:45:39,125 Better applied to things like credit card numbers, passwords, and the like. 914 00:45:39,125 --> 00:45:41,420 Phew, other questions? 915 00:45:41,420 --> 00:45:43,160 On any of this thus far? 916 00:45:43,160 --> 00:45:45,910 917 00:45:45,910 --> 00:45:47,980 Anything yet? 918 00:45:47,980 --> 00:45:50,230 No, all right, yes, in the middle. 919 00:45:50,230 --> 00:45:54,472 AUDIENCE: [INAUDIBLE] post and get, the request [INAUDIBLE]?? 920 00:45:54,472 --> 00:46:04,797 921 00:46:04,797 --> 00:46:06,130 DAVID J. MALAN: A good question. 922 00:46:06,130 --> 00:46:10,180 To repeat if you were supporting both GET and POST, 923 00:46:10,180 --> 00:46:13,210 should we have a second line that's also checking request.args? 924 00:46:13,210 --> 00:46:14,500 Yes, if you were. 925 00:46:14,500 --> 00:46:18,440 I, though, decided, at the last minute, only to support POST not GET. 926 00:46:18,440 --> 00:46:19,940 So I don't have to bother with that. 927 00:46:19,940 --> 00:46:23,380 But your question's a perfect segue to a final example of this Hello application 928 00:46:23,380 --> 00:46:27,160 where you can actually consolidate different types of functionality 929 00:46:27,160 --> 00:46:28,490 into individual routes. 930 00:46:28,490 --> 00:46:28,990 Why? 931 00:46:28,990 --> 00:46:31,240 Well, at the moment, this application is super simple. 932 00:46:31,240 --> 00:46:34,090 It's literally got one form and then one resulting page. 933 00:46:34,090 --> 00:46:37,810 But it's implemented, therefore, with a pair of routes, a pair of functions. 934 00:46:37,810 --> 00:46:39,800 No big deal for small applications. 935 00:46:39,800 --> 00:46:41,975 But if you imagine a more complicated application, 936 00:46:41,975 --> 00:46:44,350 be it Google or anything else that has many different web 937 00:46:44,350 --> 00:46:47,020 forms on different pages, it's a little annoying 938 00:46:47,020 --> 00:46:51,257 if every form needs to separate routes if only because you now 939 00:46:51,257 --> 00:46:53,590 have to keep track of literally twice as many functions. 940 00:46:53,590 --> 00:46:55,382 Your colleagues, your teaching fellow needs 941 00:46:55,382 --> 00:46:57,100 to know which one is related to which. 942 00:46:57,100 --> 00:46:58,930 So there's something to be said design-wise 943 00:46:58,930 --> 00:47:02,770 about consolidating related functionality into one single route 944 00:47:02,770 --> 00:47:04,270 so that everything is together. 945 00:47:04,270 --> 00:47:07,945 Well, we can achieve that relatively simply as follows. 946 00:47:07,945 --> 00:47:13,850 So let me go ahead and completely eliminate this greet route and simply 947 00:47:13,850 --> 00:47:16,310 have everything exist in the /route. 948 00:47:16,310 --> 00:47:19,310 And I'm going to go ahead and highlight and cut these lines out of there 949 00:47:19,310 --> 00:47:20,280 altogether. 950 00:47:20,280 --> 00:47:24,860 But if I want my single /route to support multiple methods, 951 00:47:24,860 --> 00:47:30,290 I indeed need to use methods equals and then, in square brackets, GET and POST. 952 00:47:30,290 --> 00:47:31,260 Order doesn't matter. 953 00:47:31,260 --> 00:47:33,350 But I'll keep them alphabetical in this case. 954 00:47:33,350 --> 00:47:38,540 Inside of my index route, I need to in advance is the user visiting me 955 00:47:38,540 --> 00:47:39,980 via GET or POST? 956 00:47:39,980 --> 00:47:42,470 Because if it's via GET, I want them to see the form. 957 00:47:42,470 --> 00:47:45,440 If it's via POST, I want to process, the form, that is, 958 00:47:45,440 --> 00:47:47,150 do something with the user's input. 959 00:47:47,150 --> 00:47:49,010 So it turns out it's relatively simple. 960 00:47:49,010 --> 00:47:55,470 If request.method equals equals "POST", then I can do the following. 961 00:47:55,470 --> 00:47:59,120 So you can literally check the request object, which comes with Flask, 962 00:47:59,120 --> 00:48:02,810 to figure out, was the word GET or the word POST in that virtual envelope? 963 00:48:02,810 --> 00:48:05,790 And, depending on the answer, you can do something like this. 964 00:48:05,790 --> 00:48:09,260 I can paste those lines from earlier, whereby I get 965 00:48:09,260 --> 00:48:13,100 the variable name from request.form. 966 00:48:13,100 --> 00:48:17,330 And then I render the template greet.html, passing in that name. 967 00:48:17,330 --> 00:48:18,800 Otherwise, you know what? 968 00:48:18,800 --> 00:48:22,200 I could just do else, return the template itself. 969 00:48:22,200 --> 00:48:27,410 So if the method is POST, go ahead and process the form just as we did before. 970 00:48:27,410 --> 00:48:31,790 Else, go ahead and just render the index template which contains the form. 971 00:48:31,790 --> 00:48:33,710 Strictly speaking, I don't even need the else. 972 00:48:33,710 --> 00:48:36,380 I can get rid of that, just to tighten this up a little bit, 973 00:48:36,380 --> 00:48:38,010 and unindent my last line. 974 00:48:38,010 --> 00:48:38,510 Why? 975 00:48:38,510 --> 00:48:42,860 Because recall that, from C, from Python, as soon as you return a value, 976 00:48:42,860 --> 00:48:46,045 nothing in that function is going to get executed thereafter. 977 00:48:46,045 --> 00:48:48,170 So you might as well kind of tighten up the code so 978 00:48:48,170 --> 00:48:52,770 that you don't bother adding undue indentation if not needed. 979 00:48:52,770 --> 00:48:57,810 So notice, now, if I go back to my browser, reload here, 980 00:48:57,810 --> 00:48:59,360 it's not going to work yet. 981 00:48:59,360 --> 00:49:01,640 But let's see if you can diagnose the issue. 982 00:49:01,640 --> 00:49:05,570 If I type in David here and click Greet, now I'm 983 00:49:05,570 --> 00:49:08,780 back to getting a 404 but for different reasons. 984 00:49:08,780 --> 00:49:11,570 AUDIENCE: [INAUDIBLE] 985 00:49:11,570 --> 00:49:12,065 986 00:49:12,065 --> 00:49:14,440 DAVID J. MALAN: Good, I haven't changed-- not the method. 987 00:49:14,440 --> 00:49:16,910 But I haven't changed the action in the form itself. 988 00:49:16,910 --> 00:49:20,470 So if I go back to VS Code here and I go into the web forms, 989 00:49:20,470 --> 00:49:22,600 the HTML, POST is still fine. 990 00:49:22,600 --> 00:49:24,730 But there is no /greet route anymore. 991 00:49:24,730 --> 00:49:27,205 So I actually can just specify slash. 992 00:49:27,205 --> 00:49:30,252 Or it turns out if you omit that altogether, the form will assume 993 00:49:30,252 --> 00:49:32,710 that you want to submit it to the very route from which you 994 00:49:32,710 --> 00:49:34,437 came so that is fine as well. 995 00:49:34,437 --> 00:49:37,270 I'm going to go ahead now and go back to that other tab and go back. 996 00:49:37,270 --> 00:49:38,710 I'm going to reload the page. 997 00:49:38,710 --> 00:49:40,460 And, just for good measure, this time, I'm 998 00:49:40,460 --> 00:49:43,160 going to Control-click or right-click View page source. 999 00:49:43,160 --> 00:49:45,790 And, here, yep, the action has indeed updated. 1000 00:49:45,790 --> 00:49:47,140 So I think I fixed the bug. 1001 00:49:47,140 --> 00:49:51,490 Now if I type in David and click greet, we're back in business with it working. 1002 00:49:51,490 --> 00:49:54,040 So notice that this still allows me the convenience 1003 00:49:54,040 --> 00:49:59,500 of having two separate templates, one for the form which shows the-- 1004 00:49:59,500 --> 00:50:03,010 which collects the user input and one for the actual greeting 1005 00:50:03,010 --> 00:50:04,420 which displays the user input. 1006 00:50:04,420 --> 00:50:07,120 So I'd argue that it still makes sense to keep those separate. 1007 00:50:07,120 --> 00:50:09,940 But I can avoid bloating my app.py by having 1008 00:50:09,940 --> 00:50:14,645 two methods for every single feature that I might want to implement. 1009 00:50:14,645 --> 00:50:17,030 Now, there is still a bug in this implementation 1010 00:50:17,030 --> 00:50:18,800 even though it's a little bit subtle. 1011 00:50:18,800 --> 00:50:22,850 So recall that, previously, we introduced this default value of world 1012 00:50:22,850 --> 00:50:26,900 just in case the form doesn't actually contain the word world 1013 00:50:26,900 --> 00:50:31,040 as might've happened if I didn't-- if I [INAUDIBLE] into the URL that I was 1014 00:50:31,040 --> 00:50:33,110 requesting manually as I did before. 1015 00:50:33,110 --> 00:50:36,140 But it turns out that if you're using an actual form and not, 1016 00:50:36,140 --> 00:50:39,350 of course, expecting the human to type anything into the URL bar, which 1017 00:50:39,350 --> 00:50:41,840 no human would do, it turns out that the browser is still 1018 00:50:41,840 --> 00:50:45,980 going to submit a name parameter even if its value is blank, 1019 00:50:45,980 --> 00:50:48,290 that is, empty, the so-called empty string. 1020 00:50:48,290 --> 00:50:51,290 And so even if it's the empty string, it's 1021 00:50:51,290 --> 00:50:53,090 still going to be considered to be a value 1022 00:50:53,090 --> 00:50:57,125 and, therefore, not worthy of having the default value of world plugged in. 1023 00:50:57,125 --> 00:51:01,340 In other words, if I open up my terminal window here, rerun flask run, 1024 00:51:01,340 --> 00:51:06,260 and go back over to my browser, and load this example, if I type in David, 1025 00:51:06,260 --> 00:51:09,290 as before, I'm going to be greeted with hello, David. 1026 00:51:09,290 --> 00:51:12,980 But if I try this again and don't provide an actual name but just click 1027 00:51:12,980 --> 00:51:16,190 Greet, it turns out the name parameter's still 1028 00:51:16,190 --> 00:51:20,870 going to be submitted to the server, in which case request.form.get is not 1029 00:51:20,870 --> 00:51:24,840 going to rely on the default value but rather that empty string value. 1030 00:51:24,840 --> 00:51:29,060 And so we see what appears to be a bit of an aesthetic bug hello, nothing. 1031 00:51:29,060 --> 00:51:30,920 So how can we go about fixing this? 1032 00:51:30,920 --> 00:51:35,480 Well, perhaps the simplest way is to no longer rely on this default 1033 00:51:35,480 --> 00:51:38,210 value here inside of app.py. 1034 00:51:38,210 --> 00:51:41,180 So, in fact, let me go ahead and delete that default value altogether 1035 00:51:41,180 --> 00:51:47,240 and pass name in as the variable it still is into greet.html, our template. 1036 00:51:47,240 --> 00:51:51,230 But, in greet.html, let's add a bit of logic 1037 00:51:51,230 --> 00:51:56,420 there whereby we conditionally display the name if and only if it's not empty. 1038 00:51:56,420 --> 00:52:00,980 In other words, before I output blindly name inside of these curly braces, 1039 00:52:00,980 --> 00:52:05,060 let me borrow some syntax from Python and actually use, within my Jinja 1040 00:52:05,060 --> 00:52:07,700 template, a conditional like this. 1041 00:52:07,700 --> 00:52:10,970 Open curly brace and then a percent sign because, this time, 1042 00:52:10,970 --> 00:52:13,520 I want logic, not just interpolation of a variable. 1043 00:52:13,520 --> 00:52:15,620 And I'm going to say if name. 1044 00:52:15,620 --> 00:52:19,430 And then I'm going to do another percent sign and a single curly brace. 1045 00:52:19,430 --> 00:52:23,300 And then, after that, I'm going to still use my variable name name 1046 00:52:23,300 --> 00:52:24,930 inside of two curly braces. 1047 00:52:24,930 --> 00:52:29,660 But, after that, I'm going to do again a single curly brace, a single percent 1048 00:52:29,660 --> 00:52:33,110 sign, and then I'm going to say else followed by one more percent sign. 1049 00:52:33,110 --> 00:52:36,410 And then, after that, I'm going to go ahead and actually put my default value 1050 00:52:36,410 --> 00:52:41,690 world and then close this if conditional with a single curly brace, 1051 00:52:41,690 --> 00:52:45,770 a single percent sign, and endif. 1052 00:52:45,770 --> 00:52:49,680 And then I'm going to go ahead and close that tag there. 1053 00:52:49,680 --> 00:52:53,670 So, in Jinja, it turns out that we can use it not only to plug in values. 1054 00:52:53,670 --> 00:52:56,450 We can also do a bit of lightweight conditional logic using 1055 00:52:56,450 --> 00:52:59,360 an if and an else and an endif in this case, which 1056 00:52:59,360 --> 00:53:02,300 isn't quite like Python-- indeed the endif is a little bit different. 1057 00:53:02,300 --> 00:53:04,357 But this is particular now to the Jinja template. 1058 00:53:04,357 --> 00:53:06,440 And I've done it all on one line just because this 1059 00:53:06,440 --> 00:53:09,830 is a fairly bit-sized conditional, either print out the name 1060 00:53:09,830 --> 00:53:10,880 or print out world. 1061 00:53:10,880 --> 00:53:14,750 Otherwise, I could actually put these template tags on their own lines 1062 00:53:14,750 --> 00:53:17,660 in order to spread things out all the more. 1063 00:53:17,660 --> 00:53:19,880 We'll see now, before long, that there's actually 1064 00:53:19,880 --> 00:53:24,240 some other control flow capabilities of Jinja including loops and more. 1065 00:53:24,240 --> 00:53:27,590 But, for now, this is a nice way to solve that one problem because now, 1066 00:53:27,590 --> 00:53:32,510 when I go back into my application and I go back to the form and type 1067 00:53:32,510 --> 00:53:35,690 in D-A-V-I-D, it's still going to work as expected, hello, David. 1068 00:53:35,690 --> 00:53:39,620 But if I go back one final time, type nothing in thereby sending 1069 00:53:39,620 --> 00:53:43,160 an empty value to the server and click Greet here to demonstrate as much, 1070 00:53:43,160 --> 00:53:46,430 now we do, in fact, see hello, world. 1071 00:53:46,430 --> 00:53:53,120 All right, any questions on this final example of just saying hello? 1072 00:53:53,120 --> 00:53:58,870 From those basics come pretty much all of dynamic web applications today. 1073 00:53:58,870 --> 00:53:59,370 No? 1074 00:53:59,370 --> 00:54:02,340 All right, so if you'll indulge me, here's an actual web application 1075 00:54:02,340 --> 00:54:03,970 that I made back in the day. 1076 00:54:03,970 --> 00:54:07,620 So, when I was a sophomore, I think I was not very athletic, 1077 00:54:07,620 --> 00:54:11,100 so I didn't so much do freshman intramural sports as I did run them 1078 00:54:11,100 --> 00:54:12,220 with a roommate of mine. 1079 00:54:12,220 --> 00:54:13,500 So we were sophomores in Mather House. 1080 00:54:13,500 --> 00:54:14,292 He was the athlete. 1081 00:54:14,292 --> 00:54:16,170 I was the aspiring computer scientist. 1082 00:54:16,170 --> 00:54:20,280 And so this was actually a screenshot of the very first web application 1083 00:54:20,280 --> 00:54:20,940 I ever made. 1084 00:54:20,940 --> 00:54:22,600 And this will sound old too. 1085 00:54:22,600 --> 00:54:24,750 But, back in my day, freshman year, when we 1086 00:54:24,750 --> 00:54:27,750 registered for Frosh IMs, or Freshman Intramural Sports, 1087 00:54:27,750 --> 00:54:29,790 you would literally walk across Harvard yard 1088 00:54:29,790 --> 00:54:34,923 to Wigglesworth, where a certain proctor or RA lived who was running Frosh IMs. 1089 00:54:34,923 --> 00:54:36,840 And you would literally slide a sheet of paper 1090 00:54:36,840 --> 00:54:39,270 under the door with your name on it and your choice of sports 1091 00:54:39,270 --> 00:54:40,520 that you want to register for. 1092 00:54:40,520 --> 00:54:43,210 So that was the state of the art in 1995. 1093 00:54:43,210 --> 00:54:45,630 This was ripe for disruption as people would now say. 1094 00:54:45,630 --> 00:54:50,218 And, once I actually took CS50 in the fall of 1996, which did not 1095 00:54:50,218 --> 00:54:52,260 teach, funny enough, web programming at the time, 1096 00:54:52,260 --> 00:54:55,140 I think I spent that winter or spring figuring out 1097 00:54:55,140 --> 00:54:59,692 how to do stuff with web programming, not using C and not even using Python. 1098 00:54:59,692 --> 00:55:02,650 At the time, I was using a language called Perl, which is still with us 1099 00:55:02,650 --> 00:55:05,410 but not as popular as it was back in the day. 1100 00:55:05,410 --> 00:55:08,530 But what you're seeing here is a hideous screenshot 1101 00:55:08,530 --> 00:55:10,420 of what the user interface was. 1102 00:55:10,420 --> 00:55:14,710 This was me learning how to repeat background in images infinitely, 1103 00:55:14,710 --> 00:55:16,300 no matter how big the page was. 1104 00:55:16,300 --> 00:55:19,550 Back in the day, there was no CSS, I think, even at the time. 1105 00:55:19,550 --> 00:55:22,308 So every one of these menu options was actually an image. 1106 00:55:22,308 --> 00:55:24,850 And even though-- this is a screenshot, so it's not animated. 1107 00:55:24,850 --> 00:55:27,850 If you would hover over any of these words, what 1108 00:55:27,850 --> 00:55:31,180 I would do using JavaScript, which did exist in an early form, 1109 00:55:31,180 --> 00:55:34,660 was just change the image from a blue image to a red image, 1110 00:55:34,660 --> 00:55:39,670 creating the illusion of the trickery we did last week with text decoration, 1111 00:55:39,670 --> 00:55:40,960 as you might recall in hover. 1112 00:55:40,960 --> 00:55:42,830 So the web's come a long way. 1113 00:55:42,830 --> 00:55:46,180 But this is still representative, amazingly, some 20 years later 1114 00:55:46,180 --> 00:55:48,160 of how web applications still work. 1115 00:55:48,160 --> 00:55:49,540 I used a different language. 1116 00:55:49,540 --> 00:55:52,510 I used a different backend for my data or database. 1117 00:55:52,510 --> 00:55:56,810 But everything I did then we will now do effectively today 1118 00:55:56,810 --> 00:55:59,900 and beyond because the principles have not changed. 1119 00:55:59,900 --> 00:56:03,010 It's all based ultimately on HTTP and all of the stuff 1120 00:56:03,010 --> 00:56:06,350 we discussed thus far this past week and now this. 1121 00:56:06,350 --> 00:56:09,460 So let's go ahead and make the beginnings of this website, 1122 00:56:09,460 --> 00:56:12,850 though, perhaps without as many of the hideous images underneath it. 1123 00:56:12,850 --> 00:56:17,560 In my VS Code, I'm going to go ahead and close all of my prior tabs. 1124 00:56:17,560 --> 00:56:22,060 I'll open up my terminal, and I'll hit Control-c to exit out 1125 00:56:22,060 --> 00:56:26,620 of Flask just like you can hit Control-c to exit out of the HTTP server. 1126 00:56:26,620 --> 00:56:30,700 I'm going to go ahead and hit cd to go back to my main workspace. 1127 00:56:30,700 --> 00:56:34,090 And I'm going to create a new folder with mkdir called froshims 1128 00:56:34,090 --> 00:56:37,840 so that all of my new application is inside of this folder. 1129 00:56:37,840 --> 00:56:40,180 I'm going to cd into froshims. 1130 00:56:40,180 --> 00:56:42,580 And let's go ahead and make a very simple application 1131 00:56:42,580 --> 00:56:47,103 that essentially pretends to let first years register for a sport. 1132 00:56:47,103 --> 00:56:49,270 So I'm going to need to do a bit of typing up front. 1133 00:56:49,270 --> 00:56:50,895 But I'll do the first one from scratch. 1134 00:56:50,895 --> 00:56:53,440 And then we'll start just evolving that same example. 1135 00:56:53,440 --> 00:56:57,080 Let me go ahead and do this. 1136 00:56:57,080 --> 00:56:59,290 Let me go ahead and-- 1137 00:56:59,290 --> 00:57:00,350 actually, we'll do this. 1138 00:57:00,350 --> 00:57:02,020 We'll cut one corner. 1139 00:57:02,020 --> 00:57:07,630 I'm going to go ahead and copy, from my hello example, app.py into this folder. 1140 00:57:07,630 --> 00:57:10,570 I'm going to go ahead and copy from my hello examples 1141 00:57:10,570 --> 00:57:13,120 templates my layout into this folder. 1142 00:57:13,120 --> 00:57:15,610 I'm going to create a new folder called templates. 1143 00:57:15,610 --> 00:57:18,760 I'm going to move that copied layout into templates 1144 00:57:18,760 --> 00:57:22,000 so that, at this point in the story, if I clear my screen and type ls, 1145 00:57:22,000 --> 00:57:24,062 I've got the beginnings of a web application, 1146 00:57:24,062 --> 00:57:26,020 even though it's specific to just saying hello. 1147 00:57:26,020 --> 00:57:28,810 But I'm going to go ahead and into the templates folder 1148 00:57:28,810 --> 00:57:31,450 and go into layout.html. 1149 00:57:31,450 --> 00:57:35,560 Let's just change this ever so slightly to say froshims as the title 1150 00:57:35,560 --> 00:57:38,050 just so we know we're looking at the right application. 1151 00:57:38,050 --> 00:57:43,060 And, now, let me go ahead and create a new file called how 1152 00:57:43,060 --> 00:57:46,360 about index.html inside of templates that, 1153 00:57:46,360 --> 00:57:53,860 just as before, is going to extend that there template, so extends layout.html. 1154 00:57:53,860 --> 00:57:57,400 Inside of here, I'm going to say block body just as before. 1155 00:57:57,400 --> 00:57:59,448 Preemptively going to say endblock. 1156 00:57:59,448 --> 00:58:01,240 And then, inside of here, I'm going to make 1157 00:58:01,240 --> 00:58:05,020 the beginnings of a super simple web page for first-year intramural. 1158 00:58:05,020 --> 00:58:08,290 So I'm going to use an h1 tag that's sort of big and bold that 1159 00:58:08,290 --> 00:58:11,320 just says register at the top of the page sort of like a title. 1160 00:58:11,320 --> 00:58:13,270 Below that, I'm going to have a form. 1161 00:58:13,270 --> 00:58:17,260 The action of this form I'm going to say proactively is going to say 1162 00:58:17,260 --> 00:58:18,340 to /register. 1163 00:58:18,340 --> 00:58:19,090 So that's a to do. 1164 00:58:19,090 --> 00:58:21,820 We're going to have to go implement a register route. 1165 00:58:21,820 --> 00:58:24,525 The method I'm going to use is post just for privacy's sake 1166 00:58:24,525 --> 00:58:26,650 so that if roommates are sharing the same computer, 1167 00:58:26,650 --> 00:58:29,440 they don't see, in the autocomplete, who's registered for what. 1168 00:58:29,440 --> 00:58:34,240 Inside of that form, I'm going to have a single input first where 1169 00:58:34,240 --> 00:58:37,000 autocomplete is off. 1170 00:58:37,000 --> 00:58:38,487 Autofocus is on. 1171 00:58:38,487 --> 00:58:40,570 The name of this field will be name because I want 1172 00:58:40,570 --> 00:58:43,240 to ask the humans for their human name. 1173 00:58:43,240 --> 00:58:45,610 The placeholder, just to be self-describing, 1174 00:58:45,610 --> 00:58:49,850 is going to be, quote, unquote, "Name", capital N grammatically. 1175 00:58:49,850 --> 00:58:52,600 And then, lastly, the type of this field, though it's the default, 1176 00:58:52,600 --> 00:58:53,245 is text. 1177 00:58:53,245 --> 00:58:57,400 So, so far, this is actually pretty darn similar to the hello example 1178 00:58:57,400 --> 00:58:58,750 soliciting someone's name. 1179 00:58:58,750 --> 00:59:01,630 But now I want to maybe implement a dropdown 1180 00:59:01,630 --> 00:59:03,640 menu via which you can select a sport. 1181 00:59:03,640 --> 00:59:06,910 And, back in the day, I think the first version of froshims, 1182 00:59:06,910 --> 00:59:09,650 students could only register for basketball, soccer, 1183 00:59:09,650 --> 00:59:11,000 and ultimate Frisbee. 1184 00:59:11,000 --> 00:59:12,640 So those were three of the fall sports. 1185 00:59:12,640 --> 00:59:14,210 So let me do this. 1186 00:59:14,210 --> 00:59:17,620 It's a little weirdly named, but a dropdown menu in HTML 1187 00:59:17,620 --> 00:59:20,980 is called a select menu because you select something from it. 1188 00:59:20,980 --> 00:59:25,390 The name of this input, which is really what it is, is going to be sport. 1189 00:59:25,390 --> 00:59:27,700 Though, I could call the input anything I want. 1190 00:59:27,700 --> 00:59:31,330 And, inside of this select element, I'm going to have a few options. 1191 00:59:31,330 --> 00:59:38,050 I'm going to have one where the option is how about basketball? 1192 00:59:38,050 --> 00:59:41,530 Another option, the value of which is soccer. 1193 00:59:41,530 --> 00:59:45,805 And, lastly, a third option, the value of which is ultimate Frisbee. 1194 00:59:45,805 --> 00:59:46,930 So just those three sports. 1195 00:59:46,930 --> 00:59:49,030 But suffice it to say we could add even more. 1196 00:59:49,030 --> 00:59:51,460 And then, outside of this select menu, I'm 1197 00:59:51,460 --> 00:59:55,060 going to have a button just like the hello example, the type of which 1198 00:59:55,060 --> 00:59:57,730 is submit, just to be super explicit even though that's not 1199 00:59:57,730 --> 00:59:58,820 strictly necessary. 1200 00:59:58,820 --> 01:00:01,100 But it's another attribute you'll see in the wild. 1201 01:00:01,100 --> 01:00:04,097 And then the name on the value of this button will be register. 1202 01:00:04,097 --> 01:00:05,930 So it's clear that you're not being greeted, 1203 01:00:05,930 --> 01:00:08,120 but you're actually registering for sports. 1204 01:00:08,120 --> 01:00:13,070 Now, we're not quite good to go yet, but let me go into VS code's terminal 1205 01:00:13,070 --> 01:00:13,580 again. 1206 01:00:13,580 --> 01:00:16,922 Let me open up app.py and close my terminal again. 1207 01:00:16,922 --> 01:00:19,380 And let's just whittle this down to something super simple. 1208 01:00:19,380 --> 01:00:21,088 I don't want to get overwhelmed just yet. 1209 01:00:21,088 --> 01:00:22,970 I don't want to support even POST. 1210 01:00:22,970 --> 01:00:25,730 So let's just whittle this down to the essence of this. 1211 01:00:25,730 --> 01:00:28,460 So I can do a quick check mentally and make sure now, 1212 01:00:28,460 --> 01:00:32,480 when I run flask, that I'm serving up that registration form. 1213 01:00:32,480 --> 01:00:36,980 So, in my terminal, I'm going to run flask run in my froshims folder. 1214 01:00:36,980 --> 01:00:38,270 So far, so good. 1215 01:00:38,270 --> 01:00:41,750 It's going to be by default the same URL unless I've rebuilt or created 1216 01:00:41,750 --> 01:00:42,810 a brand new codespace. 1217 01:00:42,810 --> 01:00:46,370 So let me go back to my other tab and reload that URL. 1218 01:00:46,370 --> 01:00:49,760 And, OK, we've got the beginnings of a more interesting form now. 1219 01:00:49,760 --> 01:00:51,420 So it's got place for my name. 1220 01:00:51,420 --> 01:00:54,000 It's got a dropdown for the three sports. 1221 01:00:54,000 --> 01:00:57,830 So let's see what happens, D-A-V-I-D. We'll say soccer. 1222 01:00:57,830 --> 01:01:01,310 And, when I click Register, just to be clear, what route will 1223 01:01:01,310 --> 01:01:05,045 I find myself at per my URL? 1224 01:01:05,045 --> 01:01:07,910 Slash. 1225 01:01:07,910 --> 01:01:08,970 What was it to be? 1226 01:01:08,970 --> 01:01:12,800 If I go back into my index. 1227 01:01:12,800 --> 01:01:13,880 /register. 1228 01:01:13,880 --> 01:01:18,150 But what error will I see presumably at this point in time, 1229 01:01:18,150 --> 01:01:22,470 given that app.py has only been implemented to this extent? 1230 01:01:22,470 --> 01:01:25,740 So probably 404 because the route won't be found. 1231 01:01:25,740 --> 01:01:28,850 So if I click Register, I indeed end up at /register. 1232 01:01:28,850 --> 01:01:31,703 But if I zoom in up top here, 404 not found. 1233 01:01:31,703 --> 01:01:33,870 All right, so it's the beginnings of an application. 1234 01:01:33,870 --> 01:01:36,328 But I've not-- I've implemented the front end, so to speak, 1235 01:01:36,328 --> 01:01:40,010 the user interface but not the back end, the business logic that actually 1236 01:01:40,010 --> 01:01:41,750 does something with the user input. 1237 01:01:41,750 --> 01:01:43,790 But a couple of enhancements here. 1238 01:01:43,790 --> 01:01:46,400 But these are largely niceties in HTML. 1239 01:01:46,400 --> 01:01:51,200 It's a little bad user experience that by default you're 1240 01:01:51,200 --> 01:01:52,437 registering for basketball. 1241 01:01:52,437 --> 01:01:53,270 I mean, that's fine. 1242 01:01:53,270 --> 01:01:56,360 But, arguably, you're biasing people toward registering for basketball. 1243 01:01:56,360 --> 01:01:59,610 Or they might not realize that they're registering for basketball because they 1244 01:01:59,610 --> 01:02:01,100 didn't explicitly choose a sport. 1245 01:02:01,100 --> 01:02:03,770 So having a random, an arbitrary default that 1246 01:02:03,770 --> 01:02:05,870 just happens to be the first word alphabetically 1247 01:02:05,870 --> 01:02:07,730 is a little weak when it comes to design. 1248 01:02:07,730 --> 01:02:09,313 So there's different ways to fix this. 1249 01:02:09,313 --> 01:02:12,230 But one way is to do this. 1250 01:02:12,230 --> 01:02:14,840 Add a new option at the very top. 1251 01:02:14,840 --> 01:02:18,860 But go ahead and disable it so that the user can't themselves 1252 01:02:18,860 --> 01:02:21,500 select it because you want them to select an actual sport. 1253 01:02:21,500 --> 01:02:24,720 By default, you can specify that it's indeed selected. 1254 01:02:24,720 --> 01:02:27,270 And it has no value. 1255 01:02:27,270 --> 01:02:31,660 So not to judge any sport, but this particular option has no value. 1256 01:02:31,660 --> 01:02:34,690 But what the human sees is the word sport, for instance. 1257 01:02:34,690 --> 01:02:35,850 So this is kind of a hack. 1258 01:02:35,850 --> 01:02:39,030 Ideally, the Select menu would just have a placeholder attribute 1259 01:02:39,030 --> 01:02:41,070 like the actual input boxes does. 1260 01:02:41,070 --> 01:02:43,060 But that does not exist. 1261 01:02:43,060 --> 01:02:46,480 So if I reload now, it looks a little more user friendly. 1262 01:02:46,480 --> 01:02:47,640 So it says sport. 1263 01:02:47,640 --> 01:02:49,800 I can't select sport ever again. 1264 01:02:49,800 --> 01:02:52,800 But it is the default, but I can select one of these three sports 1265 01:02:52,800 --> 01:02:55,590 which just increases the probability that the human does 1266 01:02:55,590 --> 01:02:57,328 what you might expect. 1267 01:02:57,328 --> 01:02:59,370 Of course, there's something else I can add here. 1268 01:02:59,370 --> 01:03:01,320 Suppose I don't even give my name. 1269 01:03:01,320 --> 01:03:02,760 It still went through. 1270 01:03:02,760 --> 01:03:03,930 It didn't work. 1271 01:03:03,930 --> 01:03:05,250 It's still a 405, but the-- 1272 01:03:05,250 --> 01:03:05,820 404. 1273 01:03:05,820 --> 01:03:07,830 But the browser didn't stop me. 1274 01:03:07,830 --> 01:03:10,050 So recall that we do have some other tricks. 1275 01:03:10,050 --> 01:03:13,170 For instance, I can say that this dropdown, this select menu 1276 01:03:13,170 --> 01:03:15,870 is itself required-- or, sorry, not this one. 1277 01:03:15,870 --> 01:03:19,000 The text box is itself required, for instance. 1278 01:03:19,000 --> 01:03:21,600 So now if I go back to the form and reload 1279 01:03:21,600 --> 01:03:24,900 and I just ignore the name question and click Register, 1280 01:03:24,900 --> 01:03:26,340 the browser's going to yell at me. 1281 01:03:26,340 --> 01:03:28,380 Now, recall that this is not robust. 1282 01:03:28,380 --> 01:03:30,510 Client-side validation is not good. 1283 01:03:30,510 --> 01:03:32,600 Why? 1284 01:03:32,600 --> 01:03:34,683 What'd we learn last week? 1285 01:03:34,683 --> 01:03:37,100 Yeah, I mean, I can literally right-click or Control-click 1286 01:03:37,100 --> 01:03:38,720 and open up Developer Tools. 1287 01:03:38,720 --> 01:03:42,620 I can go into that form using the Developer Tools. 1288 01:03:42,620 --> 01:03:46,820 I can literally find the word required, delete it, and voila. 1289 01:03:46,820 --> 01:03:49,670 This form will now go through because the browser's 1290 01:03:49,670 --> 01:03:50,900 going to do what I change. 1291 01:03:50,900 --> 01:03:54,950 So it's useful for user experience, making just things a little prettier 1292 01:03:54,950 --> 01:03:56,480 and faster to validate. 1293 01:03:56,480 --> 01:03:58,370 But it's not going to be robust defense. 1294 01:03:58,370 --> 01:04:02,930 All right, so let's go back now to VS Code into my actual route 1295 01:04:02,930 --> 01:04:07,140 and implement at least something here that resembles registration. 1296 01:04:07,140 --> 01:04:08,930 So I'm going to go into app.py. 1297 01:04:08,930 --> 01:04:11,300 And, in app.py, let's create this second route. 1298 01:04:11,300 --> 01:04:16,400 So, at app.route, quote, unquote, "/register" 1299 01:04:16,400 --> 01:04:18,808 to match what is in my HTML. 1300 01:04:18,808 --> 01:04:19,850 Let me define a function. 1301 01:04:19,850 --> 01:04:21,080 I can call it anything I want. 1302 01:04:21,080 --> 01:04:24,038 But, again, good convention to just call it the same thing as the route 1303 01:04:24,038 --> 01:04:26,060 name so you don't get out of sync. 1304 01:04:26,060 --> 01:04:28,520 And then there's a couple of things I might want to do. 1305 01:04:28,520 --> 01:04:31,190 When you register for this particular form, 1306 01:04:31,190 --> 01:04:36,860 what are the two things that the server should probably check for? 1307 01:04:36,860 --> 01:04:39,750 What kind of logic should I have here? 1308 01:04:39,750 --> 01:04:41,320 Yeah. 1309 01:04:41,320 --> 01:04:44,085 AUDIENCE: [INAUDIBLE] at anything for [INAUDIBLE].. 1310 01:04:44,085 --> 01:04:46,710 DAVID J. MALAN: OK, so let's make sure that the name is present 1311 01:04:46,710 --> 01:04:48,420 and the sport is present, ideally. 1312 01:04:48,420 --> 01:04:51,240 So let's actually validate the user's input just like get int 1313 01:04:51,240 --> 01:04:52,440 did back in week one. 1314 01:04:52,440 --> 01:04:56,340 Just like get string, get float, and all of those, 1315 01:04:56,340 --> 01:04:58,510 they made sure that you actually got input. 1316 01:04:58,510 --> 01:05:00,250 So there's a bunch of ways I can do this, 1317 01:05:00,250 --> 01:05:03,210 but I'm going to go ahead and take a relatively canonical approach. 1318 01:05:03,210 --> 01:05:09,450 If not request.form.get, quote, unquote, "name", 1319 01:05:09,450 --> 01:05:12,990 I'm going to go ahead and then return, how about let's just see, 1320 01:05:12,990 --> 01:05:17,320 failure, quote, unquote, "failure" just as a quick and dirty solution. 1321 01:05:17,320 --> 01:05:22,830 So if it is not the case that there is a value for the name field, 1322 01:05:22,830 --> 01:05:24,782 just assume that there's a failure. 1323 01:05:24,782 --> 01:05:25,740 So how can I test this? 1324 01:05:25,740 --> 01:05:27,370 Let me go back to the other tab. 1325 01:05:27,370 --> 01:05:30,840 Let me go ahead and not type in my name and click Register. 1326 01:05:30,840 --> 01:05:33,480 And notice-- well, OK, I need to get rid of the required 1327 01:05:33,480 --> 01:05:35,952 if I actually want to see this thing go through. 1328 01:05:35,952 --> 01:05:36,660 So you know what? 1329 01:05:36,660 --> 01:05:38,820 Let's just change the template. 1330 01:05:38,820 --> 01:05:41,130 Let's get rid of that so I don't have to hack into it 1331 01:05:41,130 --> 01:05:42,520 and delete things manually. 1332 01:05:42,520 --> 01:05:43,860 So let me reload the form. 1333 01:05:43,860 --> 01:05:45,260 Let me not type a name. 1334 01:05:45,260 --> 01:05:46,240 Click register. 1335 01:05:46,240 --> 01:05:48,220 And, oh, dang it. 1336 01:05:48,220 --> 01:05:50,740 405, Method Not Allowed. 1337 01:05:50,740 --> 01:05:54,590 What's the fix for this in my app.py? 1338 01:05:54,590 --> 01:05:56,105 What line number needs to change? 1339 01:05:56,105 --> 01:05:58,930 1340 01:05:58,930 --> 01:06:00,975 Yeah, over there. 1341 01:06:00,975 --> 01:06:01,850 AUDIENCE: [INAUDIBLE] 1342 01:06:01,850 --> 01:06:05,255 DAVID J. MALAN: Yeah, I need to allow both or at least POST at this point. 1343 01:06:05,255 --> 01:06:06,630 So I'll keep it more restrictive. 1344 01:06:06,630 --> 01:06:10,430 So methods equals and then in a list, quote, unquote, 1345 01:06:10,430 --> 01:06:14,270 POST because that's what I'm using in my template as the method. 1346 01:06:14,270 --> 01:06:15,680 All right, let's try again. 1347 01:06:15,680 --> 01:06:17,070 I'm going to go back. 1348 01:06:17,070 --> 01:06:19,850 I'm going to not type a name, and I'm going to click Register. 1349 01:06:19,850 --> 01:06:22,730 OK, so we caught the fact that the name was not provided. 1350 01:06:22,730 --> 01:06:25,220 Let's now go back and try again and actually cooperate. 1351 01:06:25,220 --> 01:06:28,830 David, Register, OK, now internal server error. 1352 01:06:28,830 --> 01:06:30,800 So something's gone even worse here. 1353 01:06:30,800 --> 01:06:32,840 And, unfortunately, you're going to start to see 1354 01:06:32,840 --> 01:06:34,710 this over the next couple of weeks. 1355 01:06:34,710 --> 01:06:39,230 This is like Python and the web's equivalent of segmentation fault. 1356 01:06:39,230 --> 01:06:42,690 It's a different issue, but it's going to hurt just the same, unfortunately. 1357 01:06:42,690 --> 01:06:44,302 So let's go back to VS Code here. 1358 01:06:44,302 --> 01:06:47,510 Nothing seems to have gone wrong, but that's because I've hidden my terminal. 1359 01:06:47,510 --> 01:06:50,450 Let me open my terminal window, and, oh, OK, so 1360 01:06:50,450 --> 01:06:54,470 it looks like I made a crazy number of mistakes here somehow. 1361 01:06:54,470 --> 01:06:58,130 But let me go ahead and focus on-- 1362 01:06:58,130 --> 01:07:00,500 and the formatting's a little weird for some reason. 1363 01:07:00,500 --> 01:07:01,340 Here we go. 1364 01:07:01,340 --> 01:07:04,290 It's a little cryptic at first glance, but here's 1365 01:07:04,290 --> 01:07:06,390 the most important line of output. 1366 01:07:06,390 --> 01:07:09,810 The view function for, quote, unquote, "Register" did not 1367 01:07:09,810 --> 01:07:11,560 return a valid response. 1368 01:07:11,560 --> 01:07:13,440 So you're not going to see this one too often 1369 01:07:13,440 --> 01:07:16,740 most likely unless you do what I did, which was you didn't have an else. 1370 01:07:16,740 --> 01:07:19,110 You didn't handle the situation where there is a name 1371 01:07:19,110 --> 01:07:20,800 and something should've come back. 1372 01:07:20,800 --> 01:07:21,900 So maybe I could do this. 1373 01:07:21,900 --> 01:07:25,007 By default, I could just say something like success as a catch 1374 01:07:25,007 --> 01:07:27,090 all even though I've not done anything useful yet. 1375 01:07:27,090 --> 01:07:28,030 Let me try this again. 1376 01:07:28,030 --> 01:07:28,920 Let me go back. 1377 01:07:28,920 --> 01:07:30,480 David is typed in. 1378 01:07:30,480 --> 01:07:32,310 No sport, Register. 1379 01:07:32,310 --> 01:07:34,110 OK, so now I'm making progress again. 1380 01:07:34,110 --> 01:07:38,980 So just like week one stuff, I make sure I'm always returning some value, 1381 01:07:38,980 --> 01:07:41,070 whether it's success or failure in this case. 1382 01:07:41,070 --> 01:07:43,200 All right, let's do something a little more interesting, though. 1383 01:07:43,200 --> 01:07:44,140 I could do this. 1384 01:07:44,140 --> 01:07:49,620 How about elif not request.form.get sport. 1385 01:07:49,620 --> 01:07:52,950 I could similarly return failure. 1386 01:07:52,950 --> 01:07:56,600 But this is a little silly to have two nearly identical conditionals. 1387 01:07:56,600 --> 01:07:58,350 So, actually, let me just tighten this up. 1388 01:07:58,350 --> 01:08:01,980 Let me go ahead and, instead, get rid of those two lines 1389 01:08:01,980 --> 01:08:09,330 and maybe just do something like this in Python or not request.form.get sport. 1390 01:08:09,330 --> 01:08:12,990 This is maybe the tightest way just to ask two questions that are essentially 1391 01:08:12,990 --> 01:08:15,060 the same but for two different keys. 1392 01:08:15,060 --> 01:08:17,850 But returning, quote, unquote, "failure"'s a little weak. 1393 01:08:17,850 --> 01:08:19,170 That's not a valid web page. 1394 01:08:19,170 --> 01:08:20,830 It's literally the word failure. 1395 01:08:20,830 --> 01:08:25,020 So maybe we do this, render_template, quote, unquote, "failure.html". 1396 01:08:25,020 --> 01:08:25,770 And you know what? 1397 01:08:25,770 --> 01:08:30,609 Down here, render_template success.html. 1398 01:08:30,609 --> 01:08:33,240 So we actually send the browser a valid web page, not just 1399 01:08:33,240 --> 01:08:34,529 a single English word. 1400 01:08:34,529 --> 01:08:36,487 Of course, we're going to need those templates. 1401 01:08:36,487 --> 01:08:39,300 So let me go in and do something like this. 1402 01:08:39,300 --> 01:08:45,630 If I go into, how about, my terminal window. 1403 01:08:45,630 --> 01:08:48,479 I need another terminal because Flask is still running in that one. 1404 01:08:48,479 --> 01:08:53,640 Let me go into froshims and let me do code of templates success.html. 1405 01:08:53,640 --> 01:08:56,859 I'm going to save a few keystrokes and copy-paste all that stuff from index. 1406 01:08:56,859 --> 01:08:58,050 But I'm going to delete most of it. 1407 01:08:58,050 --> 01:09:00,092 And I'm just going to keep it super simple today. 1408 01:09:00,092 --> 01:09:03,147 You are registered. 1409 01:09:03,147 --> 01:09:05,189 And then-- well, really, not really because we're 1410 01:09:05,189 --> 01:09:07,731 not going to bother doing anything yet with the user's input. 1411 01:09:07,731 --> 01:09:09,670 Let me do something similar now for failure. 1412 01:09:09,670 --> 01:09:13,500 So code templates failure.html. 1413 01:09:13,500 --> 01:09:15,149 I'm going to copy-paste the same thing. 1414 01:09:15,149 --> 01:09:18,040 And now I'm going to say the opposite, You are not registered. 1415 01:09:18,040 --> 01:09:20,040 But I'm not going to be very useful, and I'm not 1416 01:09:20,040 --> 01:09:22,800 going to even yet tell the user what they have done wrong. 1417 01:09:22,800 --> 01:09:25,500 But at least now we have the beginnings of a froshims app. 1418 01:09:25,500 --> 01:09:27,930 So let me go back, reload everything. 1419 01:09:27,930 --> 01:09:31,109 Let me not cooperate at all and click Register. 1420 01:09:31,109 --> 01:09:34,029 OK, so you are not registered because of some failure. 1421 01:09:34,029 --> 01:09:34,920 I'll type in my name. 1422 01:09:34,920 --> 01:09:36,370 OK, let's at least do that much. 1423 01:09:36,370 --> 01:09:37,470 I'm still not registered. 1424 01:09:37,470 --> 01:09:38,279 Let's go back. 1425 01:09:38,279 --> 01:09:40,470 Let's leave David and choose soccer. 1426 01:09:40,470 --> 01:09:43,270 Now, OK, now you are registered. 1427 01:09:43,270 --> 01:09:45,779 So I got the success template instead. 1428 01:09:45,779 --> 01:09:49,560 All right, so that seems to be better progress or at least the beginnings 1429 01:09:49,560 --> 01:09:51,750 of an actually useful application. 1430 01:09:51,750 --> 01:09:53,860 But let's actually do more validation. 1431 01:09:53,860 --> 01:09:54,360 Why? 1432 01:09:54,360 --> 01:09:56,250 Because notice what the human could still do. 1433 01:09:56,250 --> 01:09:58,800 Suppose that, out of principle, you really 1434 01:09:58,800 --> 01:10:01,780 want to register for a different sport. 1435 01:10:01,780 --> 01:10:03,750 So you're not a fan of soccer. 1436 01:10:03,750 --> 01:10:05,220 You want American football. 1437 01:10:05,220 --> 01:10:07,770 So let's right-click or Control-click on that. 1438 01:10:07,770 --> 01:10:08,880 Choose Inspect. 1439 01:10:08,880 --> 01:10:10,650 And you can even do this client side. 1440 01:10:10,650 --> 01:10:12,870 Let me write click on the Select menu. 1441 01:10:12,870 --> 01:10:15,690 In Chrome, let me select Edit as HTML. 1442 01:10:15,690 --> 01:10:18,550 You can start adding any HTML you want. 1443 01:10:18,550 --> 01:10:24,420 So let me add an option football close option enter. 1444 01:10:24,420 --> 01:10:28,600 And, aha, now you have to support football as well. 1445 01:10:28,600 --> 01:10:32,140 Of course, this is going to work because if I type in David and football 1446 01:10:32,140 --> 01:10:34,890 and Register even though I'm not doing anything with the response, 1447 01:10:34,890 --> 01:10:38,190 I got through that validation filter because I was just 1448 01:10:38,190 --> 01:10:40,210 checking that there's an actual value. 1449 01:10:40,210 --> 01:10:45,960 So this is now no longer really correct because some annoying first year who's 1450 01:10:45,960 --> 01:10:48,000 just taken CS50 is now going to do something 1451 01:10:48,000 --> 01:10:49,410 like this to my web application. 1452 01:10:49,410 --> 01:10:52,410 And we're going to have bogus data in the database, ultimately. 1453 01:10:52,410 --> 01:10:56,537 So how do you defend against this properly when it really is that easy? 1454 01:10:56,537 --> 01:10:59,370 And, honestly, as soon as you put a web application on the internet, 1455 01:10:59,370 --> 01:11:02,580 bad things will happen to it because people with too much free time. 1456 01:11:02,580 --> 01:11:06,077 So how do we defend against it? 1457 01:11:06,077 --> 01:11:07,410 What would be a better approach? 1458 01:11:07,410 --> 01:11:08,336 Yeah. 1459 01:11:08,336 --> 01:11:10,320 AUDIENCE: [INAUDIBLE] 1460 01:11:10,320 --> 01:11:13,300 1461 01:11:13,300 --> 01:11:15,730 DAVID J. MALAN: Nice, so add another conditional such 1462 01:11:15,730 --> 01:11:18,490 that the only things allowed are the sports we actually 1463 01:11:18,490 --> 01:11:19,753 are offering this semester. 1464 01:11:19,753 --> 01:11:20,920 And, in fact, you know what? 1465 01:11:20,920 --> 01:11:23,080 We can take this one step further. 1466 01:11:23,080 --> 01:11:28,210 The fact that I hardcoded into my form, my select menu, those three sports-- 1467 01:11:28,210 --> 01:11:31,340 it'd be nice to maybe factor out those sports altogether 1468 01:11:31,340 --> 01:11:34,900 so that I have one authoritative list that's used for generating the form 1469 01:11:34,900 --> 01:11:36,980 and also validating the user's input. 1470 01:11:36,980 --> 01:11:37,750 So let me do this. 1471 01:11:37,750 --> 01:11:39,790 In app.py, let me go in here. 1472 01:11:39,790 --> 01:11:43,660 And I can put this, how about, the top of my file, to be conventional. 1473 01:11:43,660 --> 01:11:46,390 I'm going to create a global variable called sports. 1474 01:11:46,390 --> 01:11:49,900 By convention, in Python, I'm going to make it all uppercase even though that 1475 01:11:49,900 --> 01:11:51,760 doesn't mean anything functional. 1476 01:11:51,760 --> 01:11:53,680 There's no const keyword in Python. 1477 01:11:53,680 --> 01:11:56,770 So it's more on the honor system that no one else should touch this. 1478 01:11:56,770 --> 01:11:59,470 But, inside of my list here, let's go ahead 1479 01:11:59,470 --> 01:12:03,380 and do only the official three, basketball, soccer, 1480 01:12:03,380 --> 01:12:05,270 and ultimate Frisbee. 1481 01:12:05,270 --> 01:12:09,340 So now I have a Python list of values that it 1482 01:12:09,340 --> 01:12:12,350 would be nice to use to generate that other form. 1483 01:12:12,350 --> 01:12:13,940 So this is maybe nonobvious. 1484 01:12:13,940 --> 01:12:16,680 But I think it's just an application of past ideas. 1485 01:12:16,680 --> 01:12:17,690 What if I do this? 1486 01:12:17,690 --> 01:12:21,140 What if I pass into my index.html template 1487 01:12:21,140 --> 01:12:25,700 a placeholder called sports and set it equal to the value 1488 01:12:25,700 --> 01:12:28,100 of that global variable sports. 1489 01:12:28,100 --> 01:12:30,950 Now, I'm trying to adhere to best practices. 1490 01:12:30,950 --> 01:12:33,300 The placeholder is called sports in lowercase. 1491 01:12:33,300 --> 01:12:36,260 But the actual variable I called all uppercase just 1492 01:12:36,260 --> 01:12:39,470 to make clear that it's a constant even though that's on the honor system. 1493 01:12:39,470 --> 01:12:40,700 But this too is conventional. 1494 01:12:40,700 --> 01:12:43,910 This is a Pythonic way or a Flask-centric way to do this. 1495 01:12:43,910 --> 01:12:48,720 But now, in index.html, this is where Jinja gets interesting. 1496 01:12:48,720 --> 01:12:51,530 This lightweight syntax for using placeholders 1497 01:12:51,530 --> 01:12:55,110 gets interesting because I can now do something like this. 1498 01:12:55,110 --> 01:12:57,170 I'm going to delete all three of the sports 1499 01:12:57,170 --> 01:13:02,180 but not the disabled option, which is just the placeholder text, inside 1500 01:13:02,180 --> 01:13:03,080 of this select menu. 1501 01:13:03,080 --> 01:13:04,310 Now I'm going to do this. 1502 01:13:04,310 --> 01:13:08,300 Just like Python, I'm going to say for sport 1503 01:13:08,300 --> 01:13:13,190 in sports using the curly brace notation and the percent signs, which 1504 01:13:13,190 --> 01:13:18,800 are Jinja specific even though Jinja and Python use almost the same syntax. 1505 01:13:18,800 --> 01:13:20,300 And that's one of the upsides of it. 1506 01:13:20,300 --> 01:13:21,592 You're not learning two things. 1507 01:13:21,592 --> 01:13:25,250 You're learning 1.1 new things. 1508 01:13:25,250 --> 01:13:27,980 endfor, which looks stupid, but this is a convention 1509 01:13:27,980 --> 01:13:31,070 in a lot of languages to literally say end and the name of the keyword 1510 01:13:31,070 --> 01:13:33,080 that you are ending with no space. 1511 01:13:33,080 --> 01:13:38,780 Inside of this Jinja loop, I'm going to write an option element once, option. 1512 01:13:38,780 --> 01:13:41,870 And then, inside of the two option tags, I'm 1513 01:13:41,870 --> 01:13:44,540 going to do my placeholder syntax with two curly braces 1514 01:13:44,540 --> 01:13:47,450 and just say sport like this. 1515 01:13:47,450 --> 01:13:52,250 And if I now go back into my browser tab and hit back here 1516 01:13:52,250 --> 01:13:55,130 and I reload the page, notice that I still 1517 01:13:55,130 --> 01:13:58,250 have a dropdown that's still automatically populated 1518 01:13:58,250 --> 01:14:02,330 because indeed if I go to View page source and look at the actual HTML, 1519 01:14:02,330 --> 01:14:04,610 there's some extra weird whitespace, but that's 1520 01:14:04,610 --> 01:14:06,230 because I hit Enter in my template. 1521 01:14:06,230 --> 01:14:09,440 And it's generating literally what I put inside of that Jinja tag. 1522 01:14:09,440 --> 01:14:12,230 It's generating that list of sports. 1523 01:14:12,230 --> 01:14:13,070 And it turns out-- 1524 01:14:13,070 --> 01:14:15,470 I'm going to do this just to be thorough. 1525 01:14:15,470 --> 01:14:18,410 It turns out that the option element technically lets 1526 01:14:18,410 --> 01:14:21,590 you specify a value for that sport. 1527 01:14:21,590 --> 01:14:23,660 Often, they're one and the same. 1528 01:14:23,660 --> 01:14:26,780 What the human sees is what the value of the option is. 1529 01:14:26,780 --> 01:14:29,660 It's kind of like the a href thing in the world of URLs. 1530 01:14:29,660 --> 01:14:31,787 But this is not going to change the functionality. 1531 01:14:31,787 --> 01:14:32,870 But it's going to do this. 1532 01:14:32,870 --> 01:14:37,910 If I reload now and I View page source, this is maybe a more common way 1533 01:14:37,910 --> 01:14:41,150 to see options where, in orange, in my browser, 1534 01:14:41,150 --> 01:14:43,160 is what the server is going to receive. 1535 01:14:43,160 --> 01:14:45,260 In white is what the human's going to see. 1536 01:14:45,260 --> 01:14:49,130 They don't have to be one and the same for reasons we'll soon see. 1537 01:14:49,130 --> 01:14:51,200 But what's nice now is that if I do actually 1538 01:14:51,200 --> 01:14:54,560 want to officially support American football, I can go in here, 1539 01:14:54,560 --> 01:14:56,840 add "football", quote, unquote, to my list, 1540 01:14:56,840 --> 01:14:59,480 go back to the form, reload, and voila. 1541 01:14:59,480 --> 01:15:01,070 Now I have a list of all four. 1542 01:15:01,070 --> 01:15:03,690 But I haven't done the second side of what you proposed, 1543 01:15:03,690 --> 01:15:05,550 which is actually validate those sports. 1544 01:15:05,550 --> 01:15:06,320 So let me do that. 1545 01:15:06,320 --> 01:15:08,000 Let me go over to app.py. 1546 01:15:08,000 --> 01:15:12,120 And, in app.py-- and we'll no longer support football there-- 1547 01:15:12,120 --> 01:15:14,480 let's do this in my registration route. 1548 01:15:14,480 --> 01:15:18,260 So, instead of just checking, is there a value? 1549 01:15:18,260 --> 01:15:20,990 And the whole point of using not is kind of like in C 1550 01:15:20,990 --> 01:15:23,520 where you use an exclamation point to invert the meaning. 1551 01:15:23,520 --> 01:15:27,650 So if it's empty but it's not, then it's-- the whole value is true. 1552 01:15:27,650 --> 01:15:29,130 Let's get rid of this line. 1553 01:15:29,130 --> 01:15:31,500 And let's instead do something like this. 1554 01:15:31,500 --> 01:15:35,810 How about if not request.form.get name. 1555 01:15:35,810 --> 01:15:37,940 So let's still just check for a name. 1556 01:15:37,940 --> 01:15:46,370 Or request.form.get, quote, unquote, "sport" is not in the sports list. 1557 01:15:46,370 --> 01:15:48,870 Now go ahead and say there's a failure. 1558 01:15:48,870 --> 01:15:49,830 So what does this mean? 1559 01:15:49,830 --> 01:15:54,365 If I go back to the browser and reload, I now see only three sports. 1560 01:15:54,365 --> 01:15:55,490 And I think this will work, 1561 01:15:55,490 --> 01:15:56,480 OK, David. 1562 01:15:56,480 --> 01:16:00,240 We'll register, say, for soccer, Register, and it seems to work. 1563 01:16:00,240 --> 01:16:02,720 But if some hacker comes along and really 1564 01:16:02,720 --> 01:16:05,810 wants to register for American football, I'll right-click there. 1565 01:16:05,810 --> 01:16:07,160 I'll inspect this. 1566 01:16:07,160 --> 01:16:10,100 I'm going to hack the form and add a bogus option 1567 01:16:10,100 --> 01:16:12,470 at the very end just for myself. 1568 01:16:12,470 --> 01:16:17,340 And, down here, I'm going to say option value equals, quote, unquote, 1569 01:16:17,340 --> 01:16:19,560 "football". 1570 01:16:19,560 --> 01:16:22,405 And then, inside of the option, I'm going 1571 01:16:22,405 --> 01:16:25,530 to say football just to be consistent even though they're one and the same. 1572 01:16:25,530 --> 01:16:26,700 Save that. 1573 01:16:26,700 --> 01:16:28,740 Close the developer tools. 1574 01:16:28,740 --> 01:16:30,780 Choose the hacked option. 1575 01:16:30,780 --> 01:16:34,240 Register, but, no, we caught it this time. 1576 01:16:34,240 --> 01:16:35,800 So this is hugely important. 1577 01:16:35,800 --> 01:16:38,490 And there are so many darn websites in the real world where 1578 01:16:38,490 --> 01:16:41,580 the programmers either don't know or don't care 1579 01:16:41,580 --> 01:16:43,920 to actually validate stuff server side. 1580 01:16:43,920 --> 01:16:46,500 This is how servers quite often get hacked. 1581 01:16:46,500 --> 01:16:50,250 You might have client-side validation using HTML or JavaScript. 1582 01:16:50,250 --> 01:16:51,000 And it looks nice. 1583 01:16:51,000 --> 01:16:51,760 It's immediate. 1584 01:16:51,760 --> 01:16:53,160 It's very pretty and graphical. 1585 01:16:53,160 --> 01:16:56,700 But if you're not also paranoically checking on the server, 1586 01:16:56,700 --> 01:16:58,500 this is indeed how servers get hacked. 1587 01:16:58,500 --> 01:17:01,170 Or, at least in the best case here, your data set 1588 01:17:01,170 --> 01:17:04,180 is sort of polluted with sports that you're not actually going to offer. 1589 01:17:04,180 --> 01:17:06,097 So this is not a very harmful attack, but it's 1590 01:17:06,097 --> 01:17:10,410 representative of what kind of actions can be taken on your server 1591 01:17:10,410 --> 01:17:12,420 if you don't distrust the user. 1592 01:17:12,420 --> 01:17:14,530 So, unfortunately, this is kind of a negative day. 1593 01:17:14,530 --> 01:17:17,470 Never, ever trust user input. 1594 01:17:17,470 --> 01:17:20,950 We saw that already with SQL and injection attacks. 1595 01:17:20,950 --> 01:17:23,080 All right, any other questions? 1596 01:17:23,080 --> 01:17:26,810 Any questions thus far on this? 1597 01:17:26,810 --> 01:17:30,340 Otherwise, we'll add a bit of spice in just a moment. 1598 01:17:30,340 --> 01:17:30,850 No? 1599 01:17:30,850 --> 01:17:32,920 All right, well, just to show you an alternative to this, 1600 01:17:32,920 --> 01:17:35,740 let me change the GUI, the Graphical User Interface, slightly. 1601 01:17:35,740 --> 01:17:37,720 Drop-down menus pretty compelling here. 1602 01:17:37,720 --> 01:17:39,340 But there's other techniques. 1603 01:17:39,340 --> 01:17:43,330 And we won't dwell on HTML tags, which you can pick up largely online. 1604 01:17:43,330 --> 01:17:47,650 But let me go into maybe index.html just to show you 1605 01:17:47,650 --> 01:17:49,870 one different approach here. 1606 01:17:49,870 --> 01:17:53,020 And if you really like radio buttons, the little circles that 1607 01:17:53,020 --> 01:17:56,650 are mutually exclusive-- this is a throwback to radios, before my time, 1608 01:17:56,650 --> 01:17:59,680 in cars where, when you pushed the button for one radio station, 1609 01:17:59,680 --> 01:18:02,200 it would pop out the buttons for another, essentially, 1610 01:18:02,200 --> 01:18:05,050 for your favorite channels. 1611 01:18:05,050 --> 01:18:08,690 Radio buttons are, by definition, therefore, mutually exclusive. 1612 01:18:08,690 --> 01:18:11,900 So if I want to see those radio buttons and not a select menu, 1613 01:18:11,900 --> 01:18:13,780 let me go into index.html. 1614 01:18:13,780 --> 01:18:17,650 And, instead of this select menu, let me actually delete that. 1615 01:18:17,650 --> 01:18:20,560 And even though this isn't going to be super pretty, let me do this. 1616 01:18:20,560 --> 01:18:26,740 for sport in sports, just as before, endfor, just preemptively. 1617 01:18:26,740 --> 01:18:29,170 Inside of this Jinja loop, I'm going to do this. 1618 01:18:29,170 --> 01:18:30,980 I'm going to do an actual input tag. 1619 01:18:30,980 --> 01:18:32,300 But it's not going to be text. 1620 01:18:32,300 --> 01:18:33,950 But the name of this tag-- 1621 01:18:33,950 --> 01:18:38,420 of this element is going to be sport. 1622 01:18:38,420 --> 01:18:43,160 The type of this element is going to be radio for radio buttons. 1623 01:18:43,160 --> 01:18:47,690 And the value of this button is going to be whatever that sport is. 1624 01:18:47,690 --> 01:18:51,320 But what the human is going to see next to the radio button to the right 1625 01:18:51,320 --> 01:18:53,147 is the same thing, the name of the sport. 1626 01:18:53,147 --> 01:18:54,980 So this is going to look a little different. 1627 01:18:54,980 --> 01:18:58,250 And it is going to look ugly in my black and white viewport here with no CSS. 1628 01:18:58,250 --> 01:19:01,820 But it does speak to how you can change the user interface just using 1629 01:19:01,820 --> 01:19:04,260 different building blocks. 1630 01:19:04,260 --> 01:19:05,420 Let me reload. 1631 01:19:05,420 --> 01:19:07,565 And, OK, it's probably not the right call here 1632 01:19:07,565 --> 01:19:09,440 because it's just kind of making things ugly. 1633 01:19:09,440 --> 01:19:13,640 But it's as simple as that because if I now click on this or this or this, 1634 01:19:13,640 --> 01:19:15,860 they're indeed mutually exclusive. 1635 01:19:15,860 --> 01:19:20,300 However, suppose that you want to allow the particularly athletic first years 1636 01:19:20,300 --> 01:19:23,120 to sign up for not one but two sports or all three. 1637 01:19:23,120 --> 01:19:25,760 In no case now can you support that right now. 1638 01:19:25,760 --> 01:19:28,250 The workaround now for a bad website would 1639 01:19:28,250 --> 01:19:31,302 be, oh, just go register twice, or go register three times. 1640 01:19:31,302 --> 01:19:33,260 It's not a huge deal because you just hit back. 1641 01:19:33,260 --> 01:19:34,880 And then you change the dropdown and submit. 1642 01:19:34,880 --> 01:19:36,780 You hit back you change the dropdown and submit. 1643 01:19:36,780 --> 01:19:38,000 But that's just bad design. 1644 01:19:38,000 --> 01:19:39,900 Surely, we can do better than that. 1645 01:19:39,900 --> 01:19:43,340 So, in fact, let's make one change here and use checkboxes. 1646 01:19:43,340 --> 01:19:46,310 And if you've never really thought hard about this in the web, 1647 01:19:46,310 --> 01:19:50,000 radio buttons and checkboxes have this distinct property 1648 01:19:50,000 --> 01:19:53,180 where the former is mutually exclusive, and the latter 1649 01:19:53,180 --> 01:19:58,100 is inclusive whereby you can check 0 or more of those boxes collectively. 1650 01:19:58,100 --> 01:20:01,580 So if I actually just go into that same template 1651 01:20:01,580 --> 01:20:06,440 and change the type of this input from radio to checkbox 1652 01:20:06,440 --> 01:20:09,590 and then go back to the browser and reload, 1653 01:20:09,590 --> 01:20:12,770 you immediately get what you and I see in the real world as checkboxes. 1654 01:20:12,770 --> 01:20:17,120 And the upside of this is that you can check now 0 or more of them. 1655 01:20:17,120 --> 01:20:19,010 But the catch-- and this is subtle-- 1656 01:20:19,010 --> 01:20:23,880 the catch with our code right now is that we're only expecting one value. 1657 01:20:23,880 --> 01:20:26,120 So it's a minor fix, but it's a useful thing to know. 1658 01:20:26,120 --> 01:20:31,010 If I go back to app.py, if I actually want to get all of the sports 1659 01:20:31,010 --> 01:20:35,300 from the users, I'm going to have to change my validation slightly. 1660 01:20:35,300 --> 01:20:36,550 So I'm going to do this. 1661 01:20:36,550 --> 01:20:39,460 I'm going to check for the presence of a name as before. 1662 01:20:39,460 --> 01:20:42,280 But then I'm going to use a loop to validate the sports because I 1663 01:20:42,280 --> 01:20:44,697 don't want them to slip, like football, back into the list 1664 01:20:44,697 --> 01:20:45,800 even if it's not there. 1665 01:20:45,800 --> 01:20:47,320 So I'm going to say this in Python. 1666 01:20:47,320 --> 01:20:52,828 for each sport in request.form.getall. 1667 01:20:52,828 --> 01:20:57,370 If you know it's a checkbox, you want to get all of the checked values, not one, 1668 01:20:57,370 --> 01:21:01,030 for the sport parameter, then go ahead and do this. 1669 01:21:01,030 --> 01:21:06,130 If the current sport is not in that sports list up top, 1670 01:21:06,130 --> 01:21:10,440 then go ahead and return render_template failure.html. 1671 01:21:10,440 --> 01:21:13,780 1672 01:21:13,780 --> 01:21:14,900 Did I make a mistake here? 1673 01:21:14,900 --> 01:21:16,340 I think we're good there. 1674 01:21:16,340 --> 01:21:19,510 So we're checking against every value that was checked on the form. 1675 01:21:19,510 --> 01:21:20,770 Is it actually valid? 1676 01:21:20,770 --> 01:21:24,850 And so now if I go in here, reload, type in my name David, and I'll 1677 01:21:24,850 --> 01:21:28,030 just check one of them, for instance, because I've not hacked the form 1678 01:21:28,030 --> 01:21:30,682 and added something bogus like football. 1679 01:21:30,682 --> 01:21:32,140 Maybe someone was alluding to this. 1680 01:21:32,140 --> 01:21:33,470 I see now an error. 1681 01:21:33,470 --> 01:21:35,057 So let's do this together. 1682 01:21:35,057 --> 01:21:36,140 Not sure what I did wrong. 1683 01:21:36,140 --> 01:21:39,370 I'm going to open up my terminal and go to here. 1684 01:21:39,370 --> 01:21:42,520 And, oh, interesting, my spacing's a little weird here. 1685 01:21:42,520 --> 01:21:43,960 But attribute error. 1686 01:21:43,960 --> 01:21:48,310 Immutable dictionary has no attribute getall. 1687 01:21:48,310 --> 01:21:51,020 So this is me lying to you. 1688 01:21:51,020 --> 01:22:02,540 1689 01:22:02,540 --> 01:22:03,740 I don't think so. 1690 01:22:03,740 --> 01:22:07,040 But [INAUDIBLE], are you here? 1691 01:22:07,040 --> 01:22:09,710 Did Flask change since I last did this? 1692 01:22:09,710 --> 01:22:12,460 No. 1693 01:22:12,460 --> 01:22:19,696 OK, so Flask post form getall. 1694 01:22:19,696 --> 01:22:21,220 All right, here we go. 1695 01:22:21,220 --> 01:22:23,650 About 2012, this is probably out of date. 1696 01:22:23,650 --> 01:22:25,750 But ah. 1697 01:22:25,750 --> 01:22:28,320 1698 01:22:28,320 --> 01:22:30,330 You know, that's not a bad idea, OK. 1699 01:22:30,330 --> 01:22:38,390 1700 01:22:38,390 --> 01:22:47,360 All right, OK, in Flask, how do I get all of the values 1701 01:22:47,360 --> 01:22:56,240 from an HTML input of type checkbox from request.form? 1702 01:22:56,240 --> 01:22:59,370 1703 01:22:59,370 --> 01:23:01,890 Well, this is horrifying. 1704 01:23:01,890 --> 01:23:02,940 getlist! 1705 01:23:02,940 --> 01:23:04,710 Damn it, OK. 1706 01:23:04,710 --> 01:23:05,940 What a good duck. 1707 01:23:05,940 --> 01:23:10,810 All right, so-- all right, so we'll rewind in time. 1708 01:23:10,810 --> 01:23:11,850 So thank you. 1709 01:23:11,850 --> 01:23:15,161 [APPLAUSE] 1710 01:23:15,161 --> 01:23:16,580 1711 01:23:16,580 --> 01:23:17,660 So that's a good lesson. 1712 01:23:17,660 --> 01:23:19,110 Just do as I do. 1713 01:23:19,110 --> 01:23:22,680 All right, so getlist will get you a list of all of those values. 1714 01:23:22,680 --> 01:23:25,550 So now if I go ahead and register as David, 1715 01:23:25,550 --> 01:23:27,530 click just soccer without injecting something 1716 01:23:27,530 --> 01:23:29,812 like American football and Register, now I'm, 1717 01:23:29,812 --> 01:23:32,270 in fact, registered but not really, not really in the sense 1718 01:23:32,270 --> 01:23:35,250 that we haven't actually done anything with the data. 1719 01:23:35,250 --> 01:23:38,540 So this is to say, ultimately, that there's a lot of these building blocks, 1720 01:23:38,540 --> 01:23:42,260 not only in HTML, which is mostly a throwback to last week but also now, 1721 01:23:42,260 --> 01:23:45,530 in Flask, where you can process all of those building blocks 1722 01:23:45,530 --> 01:23:48,200 and take control over what up, until now, 1723 01:23:48,200 --> 01:23:51,620 is usually the domain of Google or the websites that you actually use. 1724 01:23:51,620 --> 01:23:54,140 Now you actually have more of the building blocks via which 1725 01:23:54,140 --> 01:23:56,010 to implement these things yourself. 1726 01:23:56,010 --> 01:23:59,300 So let's go ahead and add some final features to froshims 1727 01:23:59,300 --> 01:24:02,150 here where we're actually doing something with the results. 1728 01:24:02,150 --> 01:24:04,650 And, for this, I'm going to open up a version in advance. 1729 01:24:04,650 --> 01:24:06,620 So I'm going to go over to VS Code here. 1730 01:24:06,620 --> 01:24:13,140 And let me go ahead and close these tabs but go into my second terminal window. 1731 01:24:13,140 --> 01:24:15,380 And I'm going to go into today's src9 directory. 1732 01:24:15,380 --> 01:24:18,120 And I'm going to go into version 4 of froshims, which has 1733 01:24:18,120 --> 01:24:20,138 everything we just did plus a bit more. 1734 01:24:20,138 --> 01:24:22,180 In particular, I'm going to go ahead and do this. 1735 01:24:22,180 --> 01:24:24,330 I'm going to show you app.py, which, additionally, 1736 01:24:24,330 --> 01:24:25,680 has some comments throughout. 1737 01:24:25,680 --> 01:24:31,860 But, in app.py, what you'll notice is that, after all of my validation, 1738 01:24:31,860 --> 01:24:34,630 I'm actually got a couple of new features here. 1739 01:24:34,630 --> 01:24:38,040 It's a little weak in terms of UI to just tell the user failure. 1740 01:24:38,040 --> 01:24:39,210 You are not registered. 1741 01:24:39,210 --> 01:24:41,010 That's all my template previously did. 1742 01:24:41,010 --> 01:24:45,360 But what if I borrow an idea from my index template where all of this time, 1743 01:24:45,360 --> 01:24:48,190 for hello and froshims, I've been passing in input. 1744 01:24:48,190 --> 01:24:49,330 So what if I do this? 1745 01:24:49,330 --> 01:24:50,190 Let me show you. 1746 01:24:50,190 --> 01:24:58,230 In templates, failure.html-- or, rather, let's see, in templates, error.html. 1747 01:24:58,230 --> 01:25:00,570 So notice this, I can make the beginnings 1748 01:25:00,570 --> 01:25:03,190 of a common format for an error page. 1749 01:25:03,190 --> 01:25:05,760 So, in error.html of this fourth example, 1750 01:25:05,760 --> 01:25:08,440 I've just got some big, bold error message at the top. 1751 01:25:08,440 --> 01:25:10,560 But I have a paragraph tag inside of which 1752 01:25:10,560 --> 01:25:13,380 is a placeholder for an error message. 1753 01:25:13,380 --> 01:25:17,520 And then I've gone one step further just because and put a happy cat or grumpy 1754 01:25:17,520 --> 01:25:21,630 cat as an image to let you down easy that something has gone wrong. 1755 01:25:21,630 --> 01:25:25,770 But this is like now every website where there's generally some customized error 1756 01:25:25,770 --> 01:25:28,410 message when something has gone wrong or when you have not 1757 01:25:28,410 --> 01:25:30,550 cooperated with the rules of the form. 1758 01:25:30,550 --> 01:25:32,230 So what am I doing instead? 1759 01:25:32,230 --> 01:25:35,820 Instead of rendering failure.html very generically, 1760 01:25:35,820 --> 01:25:38,220 I'm rendering this new template error.html. 1761 01:25:38,220 --> 01:25:39,820 And I'm passing in a custom message. 1762 01:25:39,820 --> 01:25:40,320 Why? 1763 01:25:40,320 --> 01:25:44,528 Because now, in my app.py, my logic, I can actually say, 1764 01:25:44,528 --> 01:25:45,570 you're missing your name. 1765 01:25:45,570 --> 01:25:46,528 You're missing a sport. 1766 01:25:46,528 --> 01:25:51,300 Or I can tell the human what the error, which is much better user interface. 1767 01:25:51,300 --> 01:25:53,910 Down here, though, on this new line, here's 1768 01:25:53,910 --> 01:25:57,300 where I'm now beginning to actually register registrants. 1769 01:25:57,300 --> 01:25:58,870 What's the easiest way to do this? 1770 01:25:58,870 --> 01:26:00,960 Well, let me scroll to the top of this file. 1771 01:26:00,960 --> 01:26:04,860 And you'll see that, in addition, to a big list of sports, 1772 01:26:04,860 --> 01:26:08,850 I also have an empty dictionary initially of registrants. 1773 01:26:08,850 --> 01:26:09,480 Why? 1774 01:26:09,480 --> 01:26:14,290 Well, dictionaries are this nice Swiss army knife, key-value pair, key, value, 1775 01:26:14,290 --> 01:26:15,060 key, value. 1776 01:26:15,060 --> 01:26:16,470 Names could be keys. 1777 01:26:16,470 --> 01:26:18,570 And maybe sports could be values, at least 1778 01:26:18,570 --> 01:26:20,190 if I'm supporting just single sports. 1779 01:26:20,190 --> 01:26:22,065 So I could have a fancier structure, but this 1780 01:26:22,065 --> 01:26:25,270 seems sufficient, two columns, key, value, for name, sport, name, sport, 1781 01:26:25,270 --> 01:26:26,080 and so forth. 1782 01:26:26,080 --> 01:26:31,290 So how do I put a person's name into that global dictionary? 1783 01:26:31,290 --> 01:26:35,040 Well, I'll use the syntax from week six, registrants bracket name 1784 01:26:35,040 --> 01:26:39,090 equals sport that associates that value with that key. 1785 01:26:39,090 --> 01:26:43,622 And, now, what you'll see in that I've added a new route /registrants. 1786 01:26:43,622 --> 01:26:45,330 And this is where things get interesting. 1787 01:26:45,330 --> 01:26:47,520 If I look at this premade route as you will too, 1788 01:26:47,520 --> 01:26:50,590 as you look at code that's been written for you in the weeks to come, 1789 01:26:50,590 --> 01:26:54,930 well, this sort of invites me to look at registrants.html. 1790 01:26:54,930 --> 01:26:55,650 Why? 1791 01:26:55,650 --> 01:26:58,470 Apparently, this registrants.html template 1792 01:26:58,470 --> 01:27:02,070 is being passed this global dictionary. 1793 01:27:02,070 --> 01:27:03,240 How might I use that? 1794 01:27:03,240 --> 01:27:05,910 Well, let me go into VS Code's terminal. 1795 01:27:05,910 --> 01:27:09,930 Let me take a look at registrants.html. 1796 01:27:09,930 --> 01:27:12,390 And, interesting, we haven't used this HTML much. 1797 01:27:12,390 --> 01:27:14,350 I used it super briefly last week. 1798 01:27:14,350 --> 01:27:15,305 This is an HTML table. 1799 01:27:15,305 --> 01:27:17,430 It's not going to look super pretty because I'm not 1800 01:27:17,430 --> 01:27:19,650 using bootstrap or CSS more generally. 1801 01:27:19,650 --> 01:27:23,070 But notice that, in the table's head, there's 1802 01:27:23,070 --> 01:27:26,610 name and sport from left to right in the two columns. 1803 01:27:26,610 --> 01:27:30,000 And then, in the table body or tbody, notice 1804 01:27:30,000 --> 01:27:36,570 that I have a whole bunch of tr, tr, tr, one for every registrant in that Jinja 1805 01:27:36,570 --> 01:27:37,110 loop. 1806 01:27:37,110 --> 01:27:40,830 Each of the cells, the table datas have the person's name. 1807 01:27:40,830 --> 01:27:45,000 And then if you go inside of that dictionary and look up the name, 1808 01:27:45,000 --> 01:27:48,720 you get the value thereof, so name, sport, name, sport. 1809 01:27:48,720 --> 01:27:53,550 And the route, of course, again, is just this, render registrants.html 1810 01:27:53,550 --> 01:27:55,150 by passing in that dictionary. 1811 01:27:55,150 --> 01:27:56,940 So what is registrants.html? 1812 01:27:56,940 --> 01:27:57,640 It's just this. 1813 01:27:57,640 --> 01:28:00,630 So I think if we go and run this version of the application, 1814 01:28:00,630 --> 01:28:02,350 we have some nice new features. 1815 01:28:02,350 --> 01:28:04,200 Let me go ahead and do Flask-- 1816 01:28:04,200 --> 01:28:07,530 let me kill Flask in the other window just so it's not using the same port. 1817 01:28:07,530 --> 01:28:11,040 Let me do flask run inside of froshims4. 1818 01:28:11,040 --> 01:28:12,420 So far, so good. 1819 01:28:12,420 --> 01:28:14,110 Let me go over to my other tab. 1820 01:28:14,110 --> 01:28:14,710 Let me reload. 1821 01:28:14,710 --> 01:28:15,900 So I get the latest HTML. 1822 01:28:15,900 --> 01:28:18,150 I'm going to go ahead and type in something like David 1823 01:28:18,150 --> 01:28:20,495 but select no sport using radio buttons. 1824 01:28:20,495 --> 01:28:21,870 So, again, you can only pick one. 1825 01:28:21,870 --> 01:28:25,350 And now not only am I seeing one grumpy cat there. 1826 01:28:25,350 --> 01:28:28,380 It's also telling me at the top that I'm missing the sport. 1827 01:28:28,380 --> 01:28:31,830 Conversely, if I reload the page, don't give my name. 1828 01:28:31,830 --> 01:28:33,900 But do give the sport and register. 1829 01:28:33,900 --> 01:28:36,060 Now you see that I'm missing name and not sport. 1830 01:28:36,060 --> 01:28:39,480 So, again, the UI is not very pretty, but it has the building blocks 1831 01:28:39,480 --> 01:28:41,860 of being much more appropriate. 1832 01:28:41,860 --> 01:28:43,470 Let me now cooperate on both fronts. 1833 01:28:43,470 --> 01:28:46,800 David wants to register for soccer, Register. 1834 01:28:46,800 --> 01:28:48,750 And now notice where I am. 1835 01:28:48,750 --> 01:28:51,930 Apparently, I got redirected to the registrants route, 1836 01:28:51,930 --> 01:28:54,290 inside of which is this two column table. 1837 01:28:54,290 --> 01:28:55,540 It's not very interesting yet. 1838 01:28:55,540 --> 01:28:56,940 So let me go back to the form. 1839 01:28:56,940 --> 01:28:58,860 And let me register Carter, for instance, 1840 01:28:58,860 --> 01:29:00,990 for, say, basketball, Register. 1841 01:29:00,990 --> 01:29:02,520 And now there's two of us. 1842 01:29:02,520 --> 01:29:04,410 Let me actually go back to the form. 1843 01:29:04,410 --> 01:29:07,650 And let me register Yulia for ultimate Frisbee, Register. 1844 01:29:07,650 --> 01:29:08,920 Now there's three of us. 1845 01:29:08,920 --> 01:29:11,950 And, again, the CSS is ugly, but I do have an HTML table. 1846 01:29:11,950 --> 01:29:14,790 And if I right-click and View page source, 1847 01:29:14,790 --> 01:29:18,630 you'll see David, soccer; Carter, basketball; Yulia, ultimate Frisbee all 1848 01:29:18,630 --> 01:29:20,280 as tr, tr, tr. 1849 01:29:20,280 --> 01:29:23,850 So, again, if you now think about an app like Gmail in your inbox, 1850 01:29:23,850 --> 01:29:27,010 odds are if your inbox is indeed a big table, 1851 01:29:27,010 --> 01:29:29,430 then it's probably tr, tr, tr, tr. 1852 01:29:29,430 --> 01:29:32,700 And Google is rendering all of that HTML dynamically 1853 01:29:32,700 --> 01:29:36,120 based on all of the emails in some variable somewhere. 1854 01:29:36,120 --> 01:29:39,810 Well, let me go back here and see, how did that redirect work? 1855 01:29:39,810 --> 01:29:41,580 Let's watch this a little more slowly. 1856 01:29:41,580 --> 01:29:44,060 Let me go up to the main form at slash. 1857 01:29:44,060 --> 01:29:45,540 Let me type in David. 1858 01:29:45,540 --> 01:29:47,520 Let me type in-- select soccer. 1859 01:29:47,520 --> 01:29:49,380 And let me Zoom in to the URL. 1860 01:29:49,380 --> 01:29:52,230 And notice that, when I submit this form, 1861 01:29:52,230 --> 01:29:58,560 even though the action is /register, I'm indeed ending up at /registrants. 1862 01:29:58,560 --> 01:30:00,225 So how is that actually happening? 1863 01:30:00,225 --> 01:30:02,100 Well, let me go back and do it one more time. 1864 01:30:02,100 --> 01:30:04,290 But, this time, let me open up Developer Tools. 1865 01:30:04,290 --> 01:30:07,740 Let me go to the Network tab, which, recall, we played with last week. 1866 01:30:07,740 --> 01:30:09,640 And let me go ahead and do this again. 1867 01:30:09,640 --> 01:30:13,560 So David, Soccer, and I'm going to click Register. 1868 01:30:13,560 --> 01:30:17,880 And now, notice, interesting, two routes were actually involved. 1869 01:30:17,880 --> 01:30:19,680 The first one here is Register. 1870 01:30:19,680 --> 01:30:23,160 But notice if I go to headers, ah, 302 found. 1871 01:30:23,160 --> 01:30:25,350 302 indicated some kind of redirect. 1872 01:30:25,350 --> 01:30:27,210 What's the redirect going to? 1873 01:30:27,210 --> 01:30:29,610 Well, if I look-- scroll down here at response headers, 1874 01:30:29,610 --> 01:30:31,527 there's a lot of stuff that's not interesting, 1875 01:30:31,527 --> 01:30:35,280 but location was the one we cared about last week. /registrants, oh, 1876 01:30:35,280 --> 01:30:39,660 that's why the second request over here at left is actually /registrants. 1877 01:30:39,660 --> 01:30:43,260 And it is 200 OK because it's all of these basic building 1878 01:30:43,260 --> 01:30:45,450 blocks from last week and now this. 1879 01:30:45,450 --> 01:30:47,500 Where did that redirect come from? 1880 01:30:47,500 --> 01:30:50,530 Well, now you have the ability to do this. 1881 01:30:50,530 --> 01:30:55,080 Notice that, in my register route, the last thing I said we had done 1882 01:30:55,080 --> 01:30:58,740 was add the name and the value to this global dictionary. 1883 01:30:58,740 --> 01:31:03,760 But the very last thing I did was redirect the user to the /registrants 1884 01:31:03,760 --> 01:31:04,260 route. 1885 01:31:04,260 --> 01:31:05,400 What is redirect? 1886 01:31:05,400 --> 01:31:07,500 Well, at the very top of this file, notice 1887 01:31:07,500 --> 01:31:11,940 that I proactively imported not just flask, render_template, and request. 1888 01:31:11,940 --> 01:31:14,880 I also imported redirect this time, which 1889 01:31:14,880 --> 01:31:20,580 is a function that comes with Flask that automatically issues the HTTP 302 1890 01:31:20,580 --> 01:31:25,650 redirect for you without you having to know anything about those numbers 1891 01:31:25,650 --> 01:31:27,150 or otherwise. 1892 01:31:27,150 --> 01:31:30,720 Let's do one final example before we break for snacks. 1893 01:31:30,720 --> 01:31:35,820 In this final example, froshims5, let's actually do something with SQL. 1894 01:31:35,820 --> 01:31:39,570 SQL, after all, allows us to persist the data because this version here, 1895 01:31:39,570 --> 01:31:43,740 with this global dictionary, what's the downside of using this global variable 1896 01:31:43,740 --> 01:31:46,910 to store all of our registrants? 1897 01:31:46,910 --> 01:31:47,960 What's the downside? 1898 01:31:47,960 --> 01:31:49,378 Yeah. 1899 01:31:49,378 --> 01:31:51,723 AUDIENCE: [INAUDIBLE] 1900 01:31:51,723 --> 01:31:52,657 1901 01:31:52,657 --> 01:31:54,990 DAVID J. MALAN: Exactly, so, as soon as the server quits 1902 01:31:54,990 --> 01:31:57,485 or if something goes wrong like maybe the power goes out, 1903 01:31:57,485 --> 01:31:59,610 or we have multiple servers or something like that, 1904 01:31:59,610 --> 01:32:01,920 we'll lose the contents of that dictionary. 1905 01:32:01,920 --> 01:32:04,470 And so that's not really good to store data 1906 01:32:04,470 --> 01:32:08,040 that you care about in the computer's memory alone or RAM. 1907 01:32:08,040 --> 01:32:11,973 You want to store it on disk using something like fopen and fwrite 1908 01:32:11,973 --> 01:32:13,890 and all of the file I/O stuff we talked about. 1909 01:32:13,890 --> 01:32:17,070 But, in week seven, recall, we introduced SQL. 1910 01:32:17,070 --> 01:32:19,960 So that writes things to disk in a .db file. 1911 01:32:19,960 --> 01:32:22,290 So let's actually do that with one final example. 1912 01:32:22,290 --> 01:32:26,160 Let me go ahead and close these tabs here in my terminal. 1913 01:32:26,160 --> 01:32:29,550 Let me go ahead and close the old version of froshims 1914 01:32:29,550 --> 01:32:32,040 and go into froshims5 now. 1915 01:32:32,040 --> 01:32:37,080 And, in this version, let me show you, in app.py, the following. 1916 01:32:37,080 --> 01:32:40,290 It's almost the same in terms of what we're importing from Flask. 1917 01:32:40,290 --> 01:32:43,320 But I'm also going to import from CS50's library 1918 01:32:43,320 --> 01:32:47,220 a SQL function, which we used briefly when we wrote code 1919 01:32:47,220 --> 01:32:49,600 in Python to talk to a SQLite database. 1920 01:32:49,600 --> 01:32:51,630 This is the one example of a CS50 training wheel 1921 01:32:51,630 --> 01:32:54,750 that we actually do keep on deliberately through the end of the term 1922 01:32:54,750 --> 01:32:56,500 because it's actually just really annoying 1923 01:32:56,500 --> 01:33:00,572 to use most third-party libraries with SQL in as user friendly a way. 1924 01:33:00,572 --> 01:33:03,280 You're welcome to, but I do think that, even though you shouldn't 1925 01:33:03,280 --> 01:33:06,940 be using get in getstring, getfloat anymore the SQL function's actually 1926 01:33:06,940 --> 01:33:09,450 pretty darn useful, I would say. 1927 01:33:09,450 --> 01:33:10,600 So how do we use this? 1928 01:33:10,600 --> 01:33:13,990 Everything in this file so far is pretty much the same except for that import, 1929 01:33:13,990 --> 01:33:15,890 including these lines here. 1930 01:33:15,890 --> 01:33:20,020 But notice that I am opening up a file called froshims.db. 1931 01:33:20,020 --> 01:33:22,300 And that's a database that's empty initially. 1932 01:33:22,300 --> 01:33:23,382 But it is in my account. 1933 01:33:23,382 --> 01:33:24,590 So, actually, let me do this. 1934 01:33:24,590 --> 01:33:28,180 Let me run sqlite3 on froshims.db. 1935 01:33:28,180 --> 01:33:29,950 Let me increase the size of my terminal. 1936 01:33:29,950 --> 01:33:30,790 Hit Enter. 1937 01:33:30,790 --> 01:33:35,280 What can I type to see the structure of this database? 1938 01:33:35,280 --> 01:33:37,610 Sorry. 1939 01:33:37,610 --> 01:33:38,673 Wait, what? 1940 01:33:38,673 --> 01:33:39,600 AUDIENCE: [INAUDIBLE] 1941 01:33:39,600 --> 01:33:41,058 DAVID J. MALAN: Oh, yes, thank you. 1942 01:33:41,058 --> 01:33:42,240 .schema should show me. 1943 01:33:42,240 --> 01:33:46,530 OK, it's actually a very simple database, a registrants table with one, 1944 01:33:46,530 --> 01:33:50,170 two, three columns, an ID for a unique identifier, 1945 01:33:50,170 --> 01:33:52,660 a primary key, the name of the person, and the sport 1946 01:33:52,660 --> 01:33:53,910 for which they're registering. 1947 01:33:53,910 --> 01:33:56,880 And, presumably, the ID will be automatically incremented for me. 1948 01:33:56,880 --> 01:33:58,830 So let me exit out of that. 1949 01:33:58,830 --> 01:34:00,870 Go back to app.py. 1950 01:34:00,870 --> 01:34:04,800 And this line 8 here is just giving me access to that SQLite database. 1951 01:34:04,800 --> 01:34:07,440 And recall that the three slashes are appropriate. 1952 01:34:07,440 --> 01:34:10,290 It's not a typo relative to something like a URL. 1953 01:34:10,290 --> 01:34:12,580 Here is my three sports that I want to support. 1954 01:34:12,580 --> 01:34:15,670 Looks like my index route is pretty much the same. 1955 01:34:15,670 --> 01:34:16,930 So nothing new there. 1956 01:34:16,930 --> 01:34:19,110 In fact, I'm using that same lesson as before 1957 01:34:19,110 --> 01:34:20,760 in passing in the whole sports list. 1958 01:34:20,760 --> 01:34:22,350 Notice that, OK, this is interesting. 1959 01:34:22,350 --> 01:34:26,668 Deregister, this version is going to let users sort of bow out of a sport 1960 01:34:26,668 --> 01:34:28,710 as tends to happen over the course of a semester. 1961 01:34:28,710 --> 01:34:29,877 But we'll come back to that. 1962 01:34:29,877 --> 01:34:31,530 But let's look at register now. 1963 01:34:31,530 --> 01:34:35,160 register is almost the same even though I do have some comments here. 1964 01:34:35,160 --> 01:34:37,450 We're making sure to validate the form. 1965 01:34:37,450 --> 01:34:39,250 But this is where it gets interesting. 1966 01:34:39,250 --> 01:34:43,000 I'm now inserting rows into the database to register these registrants. 1967 01:34:43,000 --> 01:34:47,290 Notice that I'm using CS50's library to insert into the registrants table 1968 01:34:47,290 --> 01:34:50,440 into these two columns name and sport, these two values. 1969 01:34:50,440 --> 01:34:54,100 And I'm being very careful to use question marks to escape the user's 1970 01:34:54,100 --> 01:34:55,990 input to avoid injection attacks. 1971 01:34:55,990 --> 01:34:58,030 And then I just redirect the user. 1972 01:34:58,030 --> 01:35:01,240 But what's going to be interesting about this version is this too, 1973 01:35:01,240 --> 01:35:05,980 /registrants no longer just uses Jinja and iterates over a global variable. 1974 01:35:05,980 --> 01:35:09,520 In this version, we're selecting all of the registrants 1975 01:35:09,520 --> 01:35:12,370 and getting back a list of dictionaries. 1976 01:35:12,370 --> 01:35:15,730 And then we're passing that list of dictionaries 1977 01:35:15,730 --> 01:35:19,690 into the Jinja template called registrants.html. 1978 01:35:19,690 --> 01:35:22,090 So, just to make clear what's going on there, 1979 01:35:22,090 --> 01:35:25,540 let me open up templates and registrants.html. 1980 01:35:25,540 --> 01:35:27,700 It's almost the same as before. 1981 01:35:27,700 --> 01:35:31,450 Notice that I'm using the dot notation this time, which Jinja also supports. 1982 01:35:31,450 --> 01:35:34,700 And it's almost always the same as the square bracket notation. 1983 01:35:34,700 --> 01:35:37,270 So you'll see both in documentation online. 1984 01:35:37,270 --> 01:35:42,133 But notice that I have a third column in the registrants table 1985 01:35:42,133 --> 01:35:43,300 that's a little interesting. 1986 01:35:43,300 --> 01:35:45,580 And this will be the final lesson for froshims. 1987 01:35:45,580 --> 01:35:50,710 A button via which people can deregister themselves, like a bow out of froshims. 1988 01:35:50,710 --> 01:35:51,560 So let's do this. 1989 01:35:51,560 --> 01:35:52,390 Open the terminal. 1990 01:35:52,390 --> 01:35:55,820 Let's do flask run in version 5 of this here. 1991 01:35:55,820 --> 01:36:01,750 Let me go into my other tab, close the Developer Tools, go to the /route, 1992 01:36:01,750 --> 01:36:04,040 and I have a form quite like before. 1993 01:36:04,040 --> 01:36:06,880 But now, when I register, David for soccer and click 1994 01:36:06,880 --> 01:36:09,490 Register, notice that it's ugly UI. 1995 01:36:09,490 --> 01:36:13,240 But there's a button next to David to deregister themselves. 1996 01:36:13,240 --> 01:36:14,350 Let's go back to slash. 1997 01:36:14,350 --> 01:36:18,100 Let me also register Carter, for instance, for basketball and so forth. 1998 01:36:18,100 --> 01:36:19,390 There's now two buttons. 1999 01:36:19,390 --> 01:36:23,350 This, now, is what really ties together our discussion of SQL 2000 01:36:23,350 --> 01:36:26,560 and primary keys with the world of the web. 2001 01:36:26,560 --> 01:36:29,560 Suppose that there were two Davids in the class, which there surely are, 2002 01:36:29,560 --> 01:36:32,380 two Carters, two Yulias, two of any names. 2003 01:36:32,380 --> 01:36:34,900 We clearly can't rely on first names alone 2004 01:36:34,900 --> 01:36:37,670 to uniquely identify humans in a room like this. 2005 01:36:37,670 --> 01:36:40,240 So we probably should use opaque identifiers, 2006 01:36:40,240 --> 01:36:42,670 that is, those numbers, 1, 2, 3. 2007 01:36:42,670 --> 01:36:44,590 Indeed, if I go into VS Code-- 2008 01:36:44,590 --> 01:36:46,690 let me open another terminal and make it bigger. 2009 01:36:46,690 --> 01:36:53,620 And, in my src9 froshims version 5, let me run sqlite3 of froshims.db. 2010 01:36:53,620 --> 01:37:00,230 And, sure enough, if I do SELECT * FROM registrants; I'll see the two of us 2011 01:37:00,230 --> 01:37:00,730 thus far. 2012 01:37:00,730 --> 01:37:02,770 And we've indeed been automatically-- been 2013 01:37:02,770 --> 01:37:06,880 assigned an auto-incrementing primary key, 1, 2, respectively. 2014 01:37:06,880 --> 01:37:11,140 That's useful now in the web especially or user interfaces in general. 2015 01:37:11,140 --> 01:37:15,100 If I view this page as source, here in my browser, 2016 01:37:15,100 --> 01:37:20,410 notice that both David and Carter have their own form in a third td 2017 01:37:20,410 --> 01:37:21,360 element next to them. 2018 01:37:21,360 --> 01:37:22,610 And that's what gives us this. 2019 01:37:22,610 --> 01:37:25,420 But notice that form, even though it's an ugly UI, 2020 01:37:25,420 --> 01:37:32,620 is a form that will post to a /deregister route a hidden input, 2021 01:37:32,620 --> 01:37:36,520 the name of which is ID to match the primary key column, 2022 01:37:36,520 --> 01:37:40,870 the value of which is 1 for me and 2 for Carter. 2023 01:37:40,870 --> 01:37:44,410 So this is how you stitch together a browser and a server. 2024 01:37:44,410 --> 01:37:46,450 When there's a database involved, you just 2025 01:37:46,450 --> 01:37:49,720 uniquely identify the things you care about by passing numbers around 2026 01:37:49,720 --> 01:37:51,940 from browser to server and back. 2027 01:37:51,940 --> 01:37:54,970 You might visually show David and Soccer and Carter and Basketball. 2028 01:37:54,970 --> 01:37:57,460 But the server only needs the unique identifier. 2029 01:37:57,460 --> 01:37:59,830 And that's why we dwelled so much, in week seven, 2030 01:37:59,830 --> 01:38:02,780 on these primary keys and, in turn, foreign keys. 2031 01:38:02,780 --> 01:38:08,020 So, when I go back to this form here and click on deregister, 2032 01:38:08,020 --> 01:38:13,270 this is going to submit ID equals 1 to the /deregister route which should-- 2033 01:38:13,270 --> 01:38:15,520 and this was the only route we didn't look at earlier. 2034 01:38:15,520 --> 01:38:17,990 Let me open up app.py again. 2035 01:38:17,990 --> 01:38:21,520 You'll see that this happens in deregister. 2036 01:38:21,520 --> 01:38:24,550 In the deregister route, which only supports POST, 2037 01:38:24,550 --> 01:38:27,460 I'm going to get the ID from the form. 2038 01:38:27,460 --> 01:38:31,780 If there is, in fact, an ID and it wasn't missing for some reason, 2039 01:38:31,780 --> 01:38:34,480 I'm going to execute delete from registrants 2040 01:38:34,480 --> 01:38:38,593 where ID equals question mark as a placeholder, passing in that number. 2041 01:38:38,593 --> 01:38:41,260 And then I'm just going to redirect the user back to registrants 2042 01:38:41,260 --> 01:38:44,450 so they can see who is still actually registered. 2043 01:38:44,450 --> 01:38:48,730 So if I go back to my browser here and I deregister myself, 2044 01:38:48,730 --> 01:38:50,350 we should see that now that's gone. 2045 01:38:50,350 --> 01:38:52,780 And if I deregister Carter, that's now gone. 2046 01:38:52,780 --> 01:38:57,640 And if I indeed go back to VS Code, open up my terminal window, make it bigger, 2047 01:38:57,640 --> 01:39:02,000 run SELECT * FROM registrants, now no one is registered for the sport. 2048 01:39:02,000 --> 01:39:05,180 And so we've effectively stitched all of these things together. 2049 01:39:05,180 --> 01:39:07,810 So that's all how we might implement froshims. 2050 01:39:07,810 --> 01:39:10,360 Just so you've heard the vocabulary, what we've implemented 2051 01:39:10,360 --> 01:39:14,500 is a paradigm known MVC, Model View Controller, 2052 01:39:14,500 --> 01:39:18,580 where the view is everything the human sees, the templates, the HTML, the CSS, 2053 01:39:18,580 --> 01:39:19,450 the JavaScript. 2054 01:39:19,450 --> 01:39:22,697 The controller is everything that's in app.py, the logic 2055 01:39:22,697 --> 01:39:24,280 that we've actually been implementing. 2056 01:39:24,280 --> 01:39:27,700 But, as soon as you introduce a database especially or even 2057 01:39:27,700 --> 01:39:31,150 a global dictionary, then you have the M in MVC, a model, which 2058 01:39:31,150 --> 01:39:32,650 is where all of your data is stored. 2059 01:39:32,650 --> 01:39:34,240 Now, you don't have to think about it this way. 2060 01:39:34,240 --> 01:39:37,060 But humans, over time, realized that, wow, most of our web apps 2061 01:39:37,060 --> 01:39:38,960 follow this similar paradigm. 2062 01:39:38,960 --> 01:39:42,160 So they started thinking about different components of the application 2063 01:39:42,160 --> 01:39:44,660 as having these different identifiers. 2064 01:39:44,660 --> 01:39:45,880 So there's still a lot more. 2065 01:39:45,880 --> 01:39:48,820 We have not yet considered how, when you log into a website, 2066 01:39:48,820 --> 01:39:50,800 the website remembers that you've logged in. 2067 01:39:50,800 --> 01:39:53,560 We've not remembered how you can keep track of what's 2068 01:39:53,560 --> 01:39:55,370 inside of someone's shopping cart. 2069 01:39:55,370 --> 01:39:57,460 This was a lot of effort just for two-second joke. 2070 01:39:57,460 --> 01:40:00,903 But let's go-- with that said, for roll-ups and snacks 2071 01:40:00,903 --> 01:40:02,570 as served, let's take a 10-minute break. 2072 01:40:02,570 --> 01:40:07,130 We'll see you in 10 for that and more as we wrap up. 2073 01:40:07,130 --> 01:40:10,630 All right, we are back, and let's consider now 2074 01:40:10,630 --> 01:40:13,210 how web applications typically work when you actually 2075 01:40:13,210 --> 01:40:17,110 have to log into them, which is most every web application nowadays. 2076 01:40:17,110 --> 01:40:20,233 Somehow or other, even though you only log in once, 2077 01:40:20,233 --> 01:40:22,900 at least at the start of the day or the start of the browser tab 2078 01:40:22,900 --> 01:40:24,760 that you open, somehow or other, websites 2079 01:40:24,760 --> 01:40:27,460 are still able to remember that you've logged in already. 2080 01:40:27,460 --> 01:40:31,100 And that's how you see your Gmail inbox or your social media feed or the like. 2081 01:40:31,100 --> 01:40:33,640 So here, for instance, is a representative login form. 2082 01:40:33,640 --> 01:40:37,270 This one here for Gmail or for all of Google services. 2083 01:40:37,270 --> 01:40:40,510 And let's consider what actually happens underneath the hood with respect 2084 01:40:40,510 --> 01:40:44,560 to those virtual envelopes when you do log in with your username and password 2085 01:40:44,560 --> 01:40:45,760 to a site like this. 2086 01:40:45,760 --> 01:40:49,270 Well, typically, inside of the virtual envelope 2087 01:40:49,270 --> 01:40:54,010 that your browser sends to Google servers, that is, accounts.google.com, 2088 01:40:54,010 --> 01:40:55,750 is a request maybe for that form. 2089 01:40:55,750 --> 01:41:00,178 So GET slash HTTP version 2 or whatever version your browser's actually working 2090 01:41:00,178 --> 01:41:01,720 and some other headers dot, dot, dot. 2091 01:41:01,720 --> 01:41:04,300 But, for the most part, that's what we've seen thus far. 2092 01:41:04,300 --> 01:41:06,333 When you then actually log in-- 2093 01:41:06,333 --> 01:41:08,500 or, rather, when you visit that page, hopefully, you 2094 01:41:08,500 --> 01:41:11,500 get back a response from the server saying that everything is OK. 2095 01:41:11,500 --> 01:41:12,760 That is 200, OK. 2096 01:41:12,760 --> 01:41:16,660 And the response that comes back is text/html, so same as last week. 2097 01:41:16,660 --> 01:41:20,060 This is just what's inside of those virtual envelopes back and forth. 2098 01:41:20,060 --> 01:41:24,130 But, when you log in to a server, it turns out, typically, 2099 01:41:24,130 --> 01:41:27,190 what's happening is that the server is, unbeknownst to you, 2100 01:41:27,190 --> 01:41:30,820 kind of stamping your hand once it's verified your username 2101 01:41:30,820 --> 01:41:34,090 and your password to remember that you have logged in. 2102 01:41:34,090 --> 01:41:36,130 In particular, what Google server is going 2103 01:41:36,130 --> 01:41:41,050 to send back after you visited that form and submitted that form as via POST, 2104 01:41:41,050 --> 01:41:43,850 is you're going to get back a response that looks like this. 2105 01:41:43,850 --> 01:41:45,730 It's going to, hopefully, say 200, OK. 2106 01:41:45,730 --> 01:41:49,390 It's probably going to be a web page written in text/html. 2107 01:41:49,390 --> 01:41:53,020 But an additional HTTP header that we didn't focus on last week, which 2108 01:41:53,020 --> 01:41:55,150 is this one, the Set-Cookie header. 2109 01:41:55,150 --> 01:41:59,140 And the Set-Cookie header specifies yet another one 2110 01:41:59,140 --> 01:42:01,690 of these key-value pairs, the name of which 2111 01:42:01,690 --> 01:42:04,990 can actually be anything depending on the server, the value of which 2112 01:42:04,990 --> 01:42:07,340 is some unique identifier. 2113 01:42:07,340 --> 01:42:10,400 So you've all probably heard about cookies in the context of the web. 2114 01:42:10,400 --> 01:42:13,240 You've probably heard that they're not good for your privacy. 2115 01:42:13,240 --> 01:42:15,310 And that's generally true. 2116 01:42:15,310 --> 01:42:19,450 But cookies need to exist if you want web pages to be 2117 01:42:19,450 --> 01:42:23,470 or websites to be stateful, that is, remember a little something about you. 2118 01:42:23,470 --> 01:42:27,760 And so session is the name of the-- 2119 01:42:27,760 --> 01:42:30,370 session is a word that describes the maintenance 2120 01:42:30,370 --> 01:42:32,503 of state between a client and a server. 2121 01:42:32,503 --> 01:42:35,170 That is to say, if the server's remembering something about you, 2122 01:42:35,170 --> 01:42:40,130 you have a session with that server, the equivalent, really, of a shopping cart. 2123 01:42:40,130 --> 01:42:43,830 So, in fact, if you go to amazon.com or any website via which you can not only 2124 01:42:43,830 --> 01:42:48,930 log in but add items to a shopping cart or equivalent, that is a session. 2125 01:42:48,930 --> 01:42:52,750 Shopping cart is the real-world equivalent thereof. 2126 01:42:52,750 --> 01:42:55,530 So this Set-Cookie header is essentially a directive 2127 01:42:55,530 --> 01:43:00,360 from the server to your browser to store this value in the browser's 2128 01:43:00,360 --> 01:43:04,200 memory somewhere, either for the life of the browser tab or maybe even longer, 2129 01:43:04,200 --> 01:43:08,010 for an hour, a day, a year, depending on the expiration time that's actually 2130 01:43:08,010 --> 01:43:08,520 set. 2131 01:43:08,520 --> 01:43:12,810 The idea, though, is that because your browser is designed to understand HTTP 2132 01:43:12,810 --> 01:43:16,650 also, just like the server, you're on the honor system, your browser, 2133 01:43:16,650 --> 01:43:20,580 such that the next time you visit Google's same server, 2134 01:43:20,580 --> 01:43:24,300 you should remind the server what cookie was set. 2135 01:43:24,300 --> 01:43:27,240 That is to say, the browser should send back to the server, 2136 01:43:27,240 --> 01:43:31,830 not set cookie because it's already been set, but a cookie header that 2137 01:43:31,830 --> 01:43:34,120 contains exactly that same value. 2138 01:43:34,120 --> 01:43:36,270 So the metaphor here is kind of like when 2139 01:43:36,270 --> 01:43:40,240 you go into maybe a bar or a club or an amusement park, 2140 01:43:40,240 --> 01:43:43,390 and you showed your ticket, or you paid your fees, ideally, 2141 01:43:43,390 --> 01:43:47,393 they'd do something like stamp your hand such that the next time you 2142 01:43:47,393 --> 01:43:50,560 go through the line, you don't have to take out your ticket again or your ID 2143 01:43:50,560 --> 01:43:53,020 and prove that you have paid or that you belong there. 2144 01:43:53,020 --> 01:43:54,370 You just show your hand stamp. 2145 01:43:54,370 --> 01:43:57,162 And the idea is that the bouncer can trust 2146 01:43:57,162 --> 01:43:58,870 that if you're presenting this hand stamp 2147 01:43:58,870 --> 01:44:02,078 and maybe it's the right color and the right picture for that particular day, 2148 01:44:02,078 --> 01:44:04,570 they should just let you in without prompting you again 2149 01:44:04,570 --> 01:44:09,380 for your ticket or your money or, in this case, your username and password. 2150 01:44:09,380 --> 01:44:12,340 So cookies are a very good thing functionally. 2151 01:44:12,340 --> 01:44:14,470 They are a feature of HTTP. 2152 01:44:14,470 --> 01:44:18,113 And they are how servers implement state between themselves and you 2153 01:44:18,113 --> 01:44:20,530 because, after all, when you click on a link on a web page 2154 01:44:20,530 --> 01:44:22,330 or another link on a web page, eventually, 2155 01:44:22,330 --> 01:44:24,040 the browser icon stops spinning. 2156 01:44:24,040 --> 01:44:27,310 And it's no longer connected to you typically. 2157 01:44:27,310 --> 01:44:31,660 But, so long as the next link you click results in a virtual envelope going 2158 01:44:31,660 --> 01:44:34,670 from client to server containing this header, 2159 01:44:34,670 --> 01:44:38,180 it's the equivalent of just reminding the server who you are. 2160 01:44:38,180 --> 01:44:42,080 This value is generally a big number, a big alphanumeric number, 2161 01:44:42,080 --> 01:44:44,150 so it's some unique identifier. 2162 01:44:44,150 --> 01:44:48,930 Generally speaking, cookies do not need to contain your actual username, 2163 01:44:48,930 --> 01:44:50,090 your actual password. 2164 01:44:50,090 --> 01:44:51,560 That's generally frowned upon. 2165 01:44:51,560 --> 01:44:53,548 The useful information like your username, 2166 01:44:53,548 --> 01:44:55,340 your password, what's in your shopping cart 2167 01:44:55,340 --> 01:44:57,260 can be stored entirely server side. 2168 01:44:57,260 --> 01:45:00,530 This cookie is just a reminder to the server who you are. 2169 01:45:00,530 --> 01:45:02,360 The problem with cookies, though, nowadays 2170 01:45:02,360 --> 01:45:05,150 is that they're used so often for advertising, 2171 01:45:05,150 --> 01:45:06,890 for tracking, and the like. 2172 01:45:06,890 --> 01:45:07,760 Why is that? 2173 01:45:07,760 --> 01:45:11,095 Well, this is a natural result of that basic primitive. 2174 01:45:11,095 --> 01:45:13,970 If your browser unbeknownst to you is in the habit of just presenting 2175 01:45:13,970 --> 01:45:16,820 this hand stamp every time it visits a website, 2176 01:45:16,820 --> 01:45:19,700 you're proactively reminding websites who 2177 01:45:19,700 --> 01:45:21,890 you are again and again, at least who you 2178 01:45:21,890 --> 01:45:24,110 are in the sense of if you logged in. 2179 01:45:24,110 --> 01:45:26,240 Now they'll always know who you are. 2180 01:45:26,240 --> 01:45:30,170 Even if you're in incognito mode, for instance, private mode browsing, 2181 01:45:30,170 --> 01:45:31,910 your hand is still getting stamped. 2182 01:45:31,910 --> 01:45:35,120 Your incognito window is still sending that same unique identifier 2183 01:45:35,120 --> 01:45:35,970 again and again. 2184 01:45:35,970 --> 01:45:37,970 And, so long as you don't log in, they might not 2185 01:45:37,970 --> 01:45:40,580 know that your Carter or David, but they do 2186 01:45:40,580 --> 01:45:45,410 know you're the same user or the same person using that browser or, rather, 2187 01:45:45,410 --> 01:45:49,040 the same browser visiting the website because that hand stamp is 2188 01:45:49,040 --> 01:45:50,730 going to be sent again and again. 2189 01:45:50,730 --> 01:45:54,350 But, when you clear your cookies or when you close your incognito window, 2190 01:45:54,350 --> 01:45:57,360 that's like washing your hand and starting fresh, 2191 01:45:57,360 --> 01:45:59,130 getting a new unique identifier. 2192 01:45:59,130 --> 01:46:02,270 So you appear to be someone different even though, as an aside, 2193 01:46:02,270 --> 01:46:04,520 even if you're in the habit of using incognito mode 2194 01:46:04,520 --> 01:46:06,950 and clearing your browser tabs and all of that, 2195 01:46:06,950 --> 01:46:11,780 with very, very high probability, servers can still track you nowadays 2196 01:46:11,780 --> 01:46:13,640 based on your IP address, of course, which 2197 01:46:13,640 --> 01:46:16,910 is on the outside of those envelopes, based on the particular browser 2198 01:46:16,910 --> 01:46:19,730 extensions that you have installed, based on the various fonts 2199 01:46:19,730 --> 01:46:20,840 that you have installed. 2200 01:46:20,840 --> 01:46:23,360 There's some crazy-high-percentage likelihood 2201 01:46:23,360 --> 01:46:26,840 that a browser can uniquely identify you even if you're scrubbing 2202 01:46:26,840 --> 01:46:28,880 your tracks in this way, so just FYI. 2203 01:46:28,880 --> 01:46:32,270 But, for today's purposes, it all derives from this thing called cookies. 2204 01:46:32,270 --> 01:46:33,920 And this is how they are then set. 2205 01:46:33,920 --> 01:46:36,740 So let's actually use this a little more productively 2206 01:46:36,740 --> 01:46:41,930 and leverage it in the context of Flask by using another global variable that 2207 01:46:41,930 --> 01:46:44,660 comes with Flask that you can import that gives you access 2208 01:46:44,660 --> 01:46:46,250 to the equivalent of a shopping cart. 2209 01:46:46,250 --> 01:46:49,130 That is to say, Flask deals with all of this stuff 2210 01:46:49,130 --> 01:46:52,610 like setting cookies and checking for cookies and all of the plumbing. 2211 01:46:52,610 --> 01:46:54,650 Someone else have solved that for you. 2212 01:46:54,650 --> 01:46:57,830 And Flask just handles the contents of the shopping cart 2213 01:46:57,830 --> 01:47:01,350 or the username to you in the context of a variable. 2214 01:47:01,350 --> 01:47:02,930 So let me go over to VS Code here. 2215 01:47:02,930 --> 01:47:05,690 And, during the break, I created a new folder called login. 2216 01:47:05,690 --> 01:47:10,082 If I type ls, I've got the beginnings of an app.py and a templates folder. 2217 01:47:10,082 --> 01:47:12,290 And, in the templates folder, I've got the beginnings 2218 01:47:12,290 --> 01:47:15,650 of a layout.html and an index.html just so I 2219 01:47:15,650 --> 01:47:18,518 don't have to type quite as many characters to get started. 2220 01:47:18,518 --> 01:47:20,310 What I'm going to do with this app, though, 2221 01:47:20,310 --> 01:47:23,510 is let's implement a very simple app that allows the user to log in 2222 01:47:23,510 --> 01:47:26,960 and demonstrates how a server can remember that you are, in fact, 2223 01:47:26,960 --> 01:47:27,690 logged in. 2224 01:47:27,690 --> 01:47:29,750 So let me open up app.py. 2225 01:47:29,750 --> 01:47:32,750 And, at the very top of this file, which is otherwise-- 2226 01:47:32,750 --> 01:47:38,600 let me shorten this even further so it looks as simple as possible. 2227 01:47:38,600 --> 01:47:44,000 In this app.py, let me go ahead and simply add one more import 2228 01:47:44,000 --> 01:47:45,800 up here called session. 2229 01:47:45,800 --> 01:47:48,800 And that's going to give me the equivalent of a shopping 2230 01:47:48,800 --> 01:47:50,030 cart at my disposal. 2231 01:47:50,030 --> 01:47:51,290 But I do need to configure it. 2232 01:47:51,290 --> 01:47:53,290 And there's some different ways to configure it. 2233 01:47:53,290 --> 01:47:55,520 But the conventional way or a recommended way 2234 01:47:55,520 --> 01:47:59,360 here is as follows, to run app.config. 2235 01:47:59,360 --> 01:48:03,680 And then set this configuration variable, which is flask specific, 2236 01:48:03,680 --> 01:48:08,180 called SESSION_PERMANENT equals false so that this will indeed 2237 01:48:08,180 --> 01:48:10,220 be treated as a session cookie. 2238 01:48:10,220 --> 01:48:13,520 So, as soon as you quit your browser or close your tabs, typically, 2239 01:48:13,520 --> 01:48:15,950 what's in the session will be deleted. 2240 01:48:15,950 --> 01:48:17,848 This does tend to vary by browser. 2241 01:48:17,848 --> 01:48:20,390 Sometimes things might be kept around longer than you expect. 2242 01:48:20,390 --> 01:48:23,432 But, by default, this is going to ensure that, essentially, the cookie is 2243 01:48:23,432 --> 01:48:25,430 deleted when you quit the browser. 2244 01:48:25,430 --> 01:48:29,870 I'm also going to do this app.config SESSION_TYPE is going 2245 01:48:29,870 --> 01:48:32,180 to equal, quote, unquote, "filesystem". 2246 01:48:32,180 --> 01:48:36,020 This just ensures that the contents of your shopping cart or equivalent 2247 01:48:36,020 --> 01:48:38,960 are stored in the servers-- 2248 01:48:38,960 --> 01:48:43,490 in the server's files, not in the cookie itself for privacy's sake. 2249 01:48:43,490 --> 01:48:46,820 And, lastly, I'm going to activate sections on this app 2250 01:48:46,820 --> 01:48:48,440 by just running this line of code. 2251 01:48:48,440 --> 01:48:51,800 These, for today's purposes, are sort of copy-paste-able lines 2252 01:48:51,800 --> 01:48:55,430 that just get sessions working in a recommended manner for you. 2253 01:48:55,430 --> 01:49:00,150 Hereafter, now, we can just use them as we expect, as we would hope. 2254 01:49:00,150 --> 01:49:01,020 So let me do this. 2255 01:49:01,020 --> 01:49:07,160 Let me now open up another file in templates called, say, index.html. 2256 01:49:07,160 --> 01:49:10,730 And, in index.html, I'm going to make a very simple web page that's 2257 01:49:10,730 --> 01:49:13,550 just going to check if the user is logged in or not 2258 01:49:13,550 --> 01:49:16,170 and explain as much if they are. 2259 01:49:16,170 --> 01:49:20,240 So, in the body block of index.html, which I prepared in advance, 2260 01:49:20,240 --> 01:49:21,350 I'm going to do this. 2261 01:49:21,350 --> 01:49:24,740 I'm going to use Jinja, not to have a loop or a placeholder, 2262 01:49:24,740 --> 01:49:27,200 but an actual conditional like this. 2263 01:49:27,200 --> 01:49:34,340 If the user is logged in with a name, then go ahead and output inside of-- 2264 01:49:34,340 --> 01:49:35,993 and let me do this else here. 2265 01:49:35,993 --> 01:49:37,910 And, actually, let me proactively do this just 2266 01:49:37,910 --> 01:49:39,410 so you can see the structure, endif. 2267 01:49:39,410 --> 01:49:43,340 So I've got the beginnings of an if else block in Jinja. 2268 01:49:43,340 --> 01:49:46,670 If there's a name variable in this template, say this, 2269 01:49:46,670 --> 01:49:50,810 You are logged in as name period. 2270 01:49:50,810 --> 01:49:53,060 And then let's give the human a link-- 2271 01:49:53,060 --> 01:49:54,500 actually, nope, let's do this. 2272 01:49:54,500 --> 01:49:58,020 Else, if you are not logged in, you are not logged in. 2273 01:49:58,020 --> 01:50:00,860 So super simple English text that's just going to tell us, 2274 01:50:00,860 --> 01:50:02,570 is the user logged in with a name? 2275 01:50:02,570 --> 01:50:03,560 Or are they not? 2276 01:50:03,560 --> 01:50:05,602 And I'm not even going to bother with a password. 2277 01:50:05,602 --> 01:50:08,270 We're going to keep it simple with just a name to demonstrate. 2278 01:50:08,270 --> 01:50:11,870 All right, so now what am I going to do in my controller, 2279 01:50:11,870 --> 01:50:13,790 that is to say, app.py? 2280 01:50:13,790 --> 01:50:16,050 Let's go ahead and do this. 2281 01:50:16,050 --> 01:50:23,810 Let's go ahead and create an app.py called login. 2282 01:50:23,810 --> 01:50:26,990 So I have a route that just handles logins for the user. 2283 01:50:26,990 --> 01:50:30,290 And I'm going to go ahead and do this. 2284 01:50:30,290 --> 01:50:34,100 Initially, I'm going to define a function called login. 2285 01:50:34,100 --> 01:50:40,220 And I'm going to have it return render template of login.html. 2286 01:50:40,220 --> 01:50:42,740 So this is going to be a login form by default. 2287 01:50:42,740 --> 01:50:43,890 Well, let's create that. 2288 01:50:43,890 --> 01:50:46,230 Let me go into my templates directory. 2289 01:50:46,230 --> 01:50:51,950 I'm going to go ahead and create a new file called login.html. 2290 01:50:51,950 --> 01:50:54,830 And, in login.html, I'm going to borrow some of this 2291 01:50:54,830 --> 01:50:56,510 HTML-- this template from before. 2292 01:50:56,510 --> 01:51:00,500 In login.html, I'm going to just have a very simple form for logging 2293 01:51:00,500 --> 01:51:03,560 in, again, no password per se, just a user's name. 2294 01:51:03,560 --> 01:51:05,600 So let's keep it simple as follows. 2295 01:51:05,600 --> 01:51:09,620 form action equals, quote, unquote, /login. 2296 01:51:09,620 --> 01:51:12,140 The method for privacy's sake will be post. 2297 01:51:12,140 --> 01:51:16,190 Inside of this form, let's just ask the user for an input with autocomplete 2298 01:51:16,190 --> 01:51:19,820 off by default and autofocus on. 2299 01:51:19,820 --> 01:51:22,520 And the name of this field will be name for the human name. 2300 01:51:22,520 --> 01:51:26,030 The placeholder text, as before, will be, quote, unquote, "Name," capital N. 2301 01:51:26,030 --> 01:51:28,917 And the type of this field will be the default, which is just text. 2302 01:51:28,917 --> 01:51:31,250 And then, lastly, let's have a button, the type of which 2303 01:51:31,250 --> 01:51:33,920 is submit because its purpose in life is to submit this form. 2304 01:51:33,920 --> 01:51:36,170 And, inside of that button, we'll see the words login. 2305 01:51:36,170 --> 01:51:38,750 So very similar to greet, very similar to register. 2306 01:51:38,750 --> 01:51:41,300 But we're now just implementing a login form. 2307 01:51:41,300 --> 01:51:43,850 OK, now let's go back to app.py. 2308 01:51:43,850 --> 01:51:47,460 The only thing login does right now is display this form. 2309 01:51:47,460 --> 01:51:50,040 But let's actually handle the user's logging in. 2310 01:51:50,040 --> 01:51:51,350 So I'm going to do this. 2311 01:51:51,350 --> 01:51:55,610 If the request.method equals, quote, unquote, "POST" 2312 01:51:55,610 --> 01:52:00,080 and, therefore, the form was logically submitted, let's do this. 2313 01:52:00,080 --> 01:52:02,910 Let's use this session variable. 2314 01:52:02,910 --> 01:52:06,140 So this session variable that I imported globally here 2315 01:52:06,140 --> 01:52:08,210 is essentially a dictionary. 2316 01:52:08,210 --> 01:52:10,730 It's an empty dictionary with two columns 2317 01:52:10,730 --> 01:52:13,050 ready to receive keys and values. 2318 01:52:13,050 --> 01:52:16,663 So if I want to put in someone's name from this login form, 2319 01:52:16,663 --> 01:52:18,080 I can just tuck it in the session. 2320 01:52:18,080 --> 01:52:23,330 And it's similar in spirit to this thing that we did last time for froshims, 2321 01:52:23,330 --> 01:52:25,340 giving myself a global dictionary. 2322 01:52:25,340 --> 01:52:28,310 But the problem with that was that, as soon as the server quits, 2323 01:52:28,310 --> 01:52:30,260 all of the memory contents are lost. 2324 01:52:30,260 --> 01:52:31,760 And it was also global. 2325 01:52:31,760 --> 01:52:35,510 So no matter whether it was me or Carter or Yulia visiting the same URL, 2326 01:52:35,510 --> 01:52:38,150 there was no distinction among users. 2327 01:52:38,150 --> 01:52:40,400 All of us were treated as the same user. 2328 01:52:40,400 --> 01:52:46,160 But what's brilliant about this session variable is that flask, because it 2329 01:52:46,160 --> 01:52:49,430 knows about HTTP and Set-Cookie and cookie, 2330 01:52:49,430 --> 01:52:53,060 it creates the illusion that whether you, the programmer, 2331 01:52:53,060 --> 01:52:57,560 have one user or a million users, the contents of session 2332 01:52:57,560 --> 01:53:01,820 are guaranteed to always be unique for me or for Carter or Yulia 2333 01:53:01,820 --> 01:53:06,140 or whatever human is visiting your code at that moment in time. 2334 01:53:06,140 --> 01:53:10,430 You have the illusion that everyone has their own session object or dictionary 2335 01:53:10,430 --> 01:53:12,680 or, really, everyone has their own shopping cart 2336 01:53:12,680 --> 01:53:15,530 just like you would expect on an amazon.com. 2337 01:53:15,530 --> 01:53:18,470 So let's use this session object like a dictionary. 2338 01:53:18,470 --> 01:53:23,150 Let's do this, let's remember the user's name from the form by going in session, 2339 01:53:23,150 --> 01:53:30,260 quote, unquote, "name" and set the value of that key equal to request.form.get 2340 01:53:30,260 --> 01:53:31,070 name. 2341 01:53:31,070 --> 01:53:36,080 So whatever's in the form, let's plop it into this global session dictionary 2342 01:53:36,080 --> 01:53:37,020 to remember the user. 2343 01:53:37,020 --> 01:53:37,783 And you know what? 2344 01:53:37,783 --> 01:53:39,700 Just for user interfaces' sake, let's do this, 2345 01:53:39,700 --> 01:53:44,160 let's return a redirect, a 302 or whatever, to slash. 2346 01:53:44,160 --> 01:53:47,910 Let's just redirect the user back to the home page to see if they are, in fact, 2347 01:53:47,910 --> 01:53:49,540 logged in at that point. 2348 01:53:49,540 --> 01:53:53,250 So I think, at this point, that's the entirety of my login route. 2349 01:53:53,250 --> 01:53:57,390 If the user has submitted their name via POST, store their name in the session, 2350 01:53:57,390 --> 01:53:59,760 and then redirect the user back to the home page. 2351 01:53:59,760 --> 01:54:02,670 The home page meanwhile looks again like this. 2352 01:54:02,670 --> 01:54:07,083 If there is a name in this template, we're going to see that person's name. 2353 01:54:07,083 --> 01:54:09,250 Else, we're going to see that they're not logged in. 2354 01:54:09,250 --> 01:54:10,620 So how do we get at that name? 2355 01:54:10,620 --> 01:54:11,790 Back to app.py. 2356 01:54:11,790 --> 01:54:13,620 Let's just assemble these building blocks. 2357 01:54:13,620 --> 01:54:16,980 In my index route, I'm going to do this. 2358 01:54:16,980 --> 01:54:22,020 name equals session.get, quote, unquote, name. 2359 01:54:22,020 --> 01:54:26,490 So you can treat session similar to request.args, similar to request.form. 2360 01:54:26,490 --> 01:54:28,620 But it's a more permanent thing. 2361 01:54:28,620 --> 01:54:31,345 At least so long as it's the same human logged in, 2362 01:54:31,345 --> 01:54:33,720 I'm going to get my name or Carter's name or Yulia's name 2363 01:54:33,720 --> 01:54:38,013 depending on whose browser is visiting my URL at this moment in time. 2364 01:54:38,013 --> 01:54:39,930 All right, so let's cross my fingers because I 2365 01:54:39,930 --> 01:54:41,650 wrote a lot of this on the fly. 2366 01:54:41,650 --> 01:54:46,830 Let me open my terminal window in my login folder and do flask run. 2367 01:54:46,830 --> 01:54:48,960 And, OK, I screwed up already. 2368 01:54:48,960 --> 01:54:50,340 Session is not defined. 2369 01:54:50,340 --> 01:54:52,050 Did I mean session in lowercase? 2370 01:54:52,050 --> 01:54:53,590 No, not in this case. 2371 01:54:53,590 --> 01:55:00,210 It turns out what I should have done is one more line from flask_session import 2372 01:55:00,210 --> 01:55:00,900 Session. 2373 01:55:00,900 --> 01:55:01,470 Why? 2374 01:55:01,470 --> 01:55:02,850 This is another flask library. 2375 01:55:02,850 --> 01:55:05,433 It's technically a third-party library that other smart people 2376 01:55:05,433 --> 01:55:07,390 wrote for flask users to use. 2377 01:55:07,390 --> 01:55:10,960 So I copied and pasted that line from the documentation. 2378 01:55:10,960 --> 01:55:12,125 So clearly forgot about it. 2379 01:55:12,125 --> 01:55:14,250 Let me get rid of this registrants dictionary which 2380 01:55:14,250 --> 01:55:15,780 has nothing to do with this example. 2381 01:55:15,780 --> 01:55:20,070 Let me now open my terminal again and do flask run. 2382 01:55:20,070 --> 01:55:21,630 OK, now we seem to be in good shape. 2383 01:55:21,630 --> 01:55:22,980 No error message is apparent. 2384 01:55:22,980 --> 01:55:25,080 Let me go back to my URL and reload. 2385 01:55:25,080 --> 01:55:26,748 And notice I am not logged in. 2386 01:55:26,748 --> 01:55:29,790 Now, this is not very user friendly because the only way I know I can log 2387 01:55:29,790 --> 01:55:31,620 in is by going to /login. 2388 01:55:31,620 --> 01:55:33,210 So let's actually improve the UI. 2389 01:55:33,210 --> 01:55:37,970 In index.html, if you are not logged in, let's do this. 2390 01:55:37,970 --> 01:55:41,390 a href equals /login. 2391 01:55:41,390 --> 01:55:43,080 And let's give them a link to log in. 2392 01:55:43,080 --> 01:55:44,330 And, otherwise, you know what? 2393 01:55:44,330 --> 01:55:45,290 Let's do this. 2394 01:55:45,290 --> 01:55:49,760 a href equals, quote, unquote, /logout even though it doesn't exist yet if we 2395 01:55:49,760 --> 01:55:52,640 want to facilitate the user logging out. 2396 01:55:52,640 --> 01:55:55,550 But I think this will be a nicer UI because if I reload now, 2397 01:55:55,550 --> 01:55:57,080 I'm told that I'm not logged in. 2398 01:55:57,080 --> 01:55:59,690 But there's my new login link shown conditionally. 2399 01:55:59,690 --> 01:56:00,560 Well, let's do this. 2400 01:56:00,560 --> 01:56:02,887 If I click on this, it's super small. 2401 01:56:02,887 --> 01:56:04,970 But, in the bottom left-hand corner of my browser, 2402 01:56:04,970 --> 01:56:06,860 I'm seeing the /login route. 2403 01:56:06,860 --> 01:56:11,030 If I click on that there is that login form, no password, super simple, 2404 01:56:11,030 --> 01:56:11,940 just a name. 2405 01:56:11,940 --> 01:56:16,550 I'll type in D-A-V-I-D. And now, notice, if I Zoom in here, 2406 01:56:16,550 --> 01:56:19,080 I'm indeed currently at the /login route. 2407 01:56:19,080 --> 01:56:21,560 But, when I log in, I'm going to be-- 2408 01:56:21,560 --> 01:56:22,400 oh, damn it. 2409 01:56:22,400 --> 01:56:23,960 I did make a mistake. 2410 01:56:23,960 --> 01:56:25,332 405 method not allowed. 2411 01:56:25,332 --> 01:56:26,540 I think we fixed this before. 2412 01:56:26,540 --> 01:56:28,010 I can go back into app.py. 2413 01:56:28,010 --> 01:56:31,790 Which line number do I need to fix? 2414 01:56:31,790 --> 01:56:35,150 Method was not supported for which route? 2415 01:56:35,150 --> 01:56:36,500 So, yeah, line 16. 2416 01:56:36,500 --> 01:56:39,350 So I think I need methods equals both of them. 2417 01:56:39,350 --> 01:56:41,730 So GET so I can display the form. 2418 01:56:41,730 --> 01:56:44,175 And POST so I can process the form. 2419 01:56:44,175 --> 01:56:45,050 Let's try this again. 2420 01:56:45,050 --> 01:56:46,520 I'm just going to go back. 2421 01:56:46,520 --> 01:56:47,990 And I'm going to click-- 2422 01:56:47,990 --> 01:56:49,730 type David and click Log In. 2423 01:56:49,730 --> 01:56:51,740 And I'll zoom in on the URL. 2424 01:56:51,740 --> 01:56:55,220 Log In, ah, you are logged in as David. 2425 01:56:55,220 --> 01:56:56,850 Now I can log out. 2426 01:56:56,850 --> 01:56:57,950 And, indeed, if I log out. 2427 01:56:57,950 --> 01:56:59,158 This one's not going to work. 2428 01:56:59,158 --> 01:57:03,030 I think I'm going to get a 404 not found, but that's fixable too. 2429 01:57:03,030 --> 01:57:04,700 So let me go back into VS Code. 2430 01:57:04,700 --> 01:57:06,660 Let me go down to the bottom of the file. 2431 01:57:06,660 --> 01:57:09,980 And let's add another route /logout. 2432 01:57:09,980 --> 01:57:12,620 Just GET is fine because I'm not posting anything to it. 2433 01:57:12,620 --> 01:57:15,698 I'll define a function called logout just to match the name of the route. 2434 01:57:15,698 --> 01:57:18,240 And you wouldn't know this without reading the documentation, 2435 01:57:18,240 --> 01:57:19,940 but if you want to clear the session and forget 2436 01:57:19,940 --> 01:57:22,430 the user's name, forget the items in their shopping cart, 2437 01:57:22,430 --> 01:57:24,103 you can do session.clear. 2438 01:57:24,103 --> 01:57:26,270 And that will just clear the contents of the session 2439 01:57:26,270 --> 01:57:28,760 wherever the server is storing them. 2440 01:57:28,760 --> 01:57:31,670 And then I can just return a redirect. 2441 01:57:31,670 --> 01:57:34,610 redirect to, for instance, slash. 2442 01:57:34,610 --> 01:57:35,910 So let's see this in action. 2443 01:57:35,910 --> 01:57:37,800 Let me go back to the browser and hit back. 2444 01:57:37,800 --> 01:57:39,000 I'm logged in as David. 2445 01:57:39,000 --> 01:57:41,940 But now, when I click Log out, I'm going to quickly go to-- 2446 01:57:41,940 --> 01:57:43,890 and I'll show you in the inspector. 2447 01:57:43,890 --> 01:57:46,560 Inspect, Network tab. 2448 01:57:46,560 --> 01:57:48,150 Let's click on Log out. 2449 01:57:48,150 --> 01:57:50,980 I indeed end up at logout first. 2450 01:57:50,980 --> 01:57:54,510 But then I'm redirected to this long URL, which is unique to my codespace. 2451 01:57:54,510 --> 01:58:00,270 But the first logout URL redirects me with a 302 until I see a 200. 2452 01:58:00,270 --> 01:58:02,770 And I'm back at the home screen. 2453 01:58:02,770 --> 01:58:05,940 So, even though that was a bunch of steps for me to do, 2454 01:58:05,940 --> 01:58:11,530 that is how every website implements the notion of logins and also shopping 2455 01:58:11,530 --> 01:58:12,030 carts. 2456 01:58:12,030 --> 01:58:15,570 Now, you might add some fanciness with usernames and passwords 2457 01:58:15,570 --> 01:58:18,060 and the like, maybe two-factor authentication and the like. 2458 01:58:18,060 --> 01:58:21,300 But it all boils down to storing stuff in the session 2459 01:58:21,300 --> 01:58:26,700 and using those cookie headers in order to keep track of who is who. 2460 01:58:26,700 --> 01:58:30,135 Any questions on these techniques thus far? 2461 01:58:30,135 --> 01:58:33,080 2462 01:58:33,080 --> 01:58:33,580 No? 2463 01:58:33,580 --> 01:58:36,820 All right, how about an actual Amazon, if a simplistic one? 2464 01:58:36,820 --> 01:58:37,460 Let me do this. 2465 01:58:37,460 --> 01:58:39,230 Let me go back to VS Code here. 2466 01:58:39,230 --> 01:58:41,620 Let me close these tabs, and let me open up an example 2467 01:58:41,620 --> 01:58:46,510 that I wrote in advance, this one in my src9 directory's store folder, 2468 01:58:46,510 --> 01:58:49,270 so the beginnings of an electronic commerce store online. 2469 01:58:49,270 --> 01:58:53,230 Let me just dive in blindly and do flask run for this one. 2470 01:58:53,230 --> 01:58:56,380 And let me go to my browser tab and reload because I'm 2471 01:58:56,380 --> 01:58:57,760 going to see my new app now. 2472 01:58:57,760 --> 01:58:59,560 And it's super simple, also ugly. 2473 01:58:59,560 --> 01:59:01,450 There's no CSS or fanciness there. 2474 01:59:01,450 --> 01:59:02,770 But it's just HTML. 2475 01:59:02,770 --> 01:59:05,950 And this is the earliest version, if you will, of Amazon 1.0 2476 01:59:05,950 --> 01:59:09,770 where they just sold books, and only these five books, for instance. 2477 01:59:09,770 --> 01:59:11,110 So what's going on here? 2478 01:59:11,110 --> 01:59:14,080 Well, here is how using all of today's primitives, 2479 01:59:14,080 --> 01:59:16,010 you can start to infer how websites work. 2480 01:59:16,010 --> 01:59:17,410 So let me View page source. 2481 01:59:17,410 --> 01:59:21,130 And, even though the UI is not very pretty, let's see what's inside here. 2482 01:59:21,130 --> 01:59:24,490 There's an h1 tag for books, just like the title of the page. 2483 01:59:24,490 --> 01:59:26,860 There's a whole bunch of h2s, each one of which 2484 01:59:26,860 --> 01:59:28,780 represents apparently the title of a book. 2485 01:59:28,780 --> 01:59:31,540 And below every title is a unique form. 2486 01:59:31,540 --> 01:59:34,970 Every form, though, has an action of /cart. 2487 01:59:34,970 --> 01:59:37,983 And every one submits via post to that cart. 2488 01:59:37,983 --> 01:59:38,900 But notice this trick. 2489 01:59:38,900 --> 01:59:43,460 Just like the deregistration for froshims, every one of these forms 2490 01:59:43,460 --> 01:59:48,380 has a unique ID that's hidden, but the value for this one is 1. 2491 01:59:48,380 --> 01:59:50,030 The value for this one is 2. 2492 01:59:50,030 --> 01:59:51,620 The value for this one is 3. 2493 01:59:51,620 --> 01:59:54,620 So even though for froshims, we used it to deregister people 2494 01:59:54,620 --> 01:59:59,030 to opt out of the froshims, here, you're using it to effectively add items 2495 01:59:59,030 --> 01:59:59,990 to your cart. 2496 01:59:59,990 --> 02:00:00,650 Why? 2497 02:00:00,650 --> 02:00:03,080 Well, where is this data coming from? 2498 02:00:03,080 --> 02:00:04,250 Let's poke around further. 2499 02:00:04,250 --> 02:00:06,590 I'm going to open another terminal window in VS Code. 2500 02:00:06,590 --> 02:00:07,460 I'll make it bigger. 2501 02:00:07,460 --> 02:00:09,830 I'm going to go into src9 and store. 2502 02:00:09,830 --> 02:00:15,110 And if I type ls, you'll see app.py, requirements.txt, templates, 2503 02:00:15,110 --> 02:00:16,700 all of which I predicted would exist. 2504 02:00:16,700 --> 02:00:19,860 There's a temporary folder called flask_session. 2505 02:00:19,860 --> 02:00:22,550 This is where, surprise, surprise, your sessions 2506 02:00:22,550 --> 02:00:25,080 are temporarily stored on the server. 2507 02:00:25,080 --> 02:00:28,280 So even if the computer quits or reboots, the files are still there. 2508 02:00:28,280 --> 02:00:31,610 The shopping carts are still there, but you shouldn't need to go in there. 2509 02:00:31,610 --> 02:00:34,527 And definitely don't change things in there because things will break. 2510 02:00:34,527 --> 02:00:37,460 But notice store.db, which came with my folder today. 2511 02:00:37,460 --> 02:00:39,956 Let me run sqlite3 of store.db. 2512 02:00:39,956 --> 02:00:42,230 .schema to see what's going on in there. 2513 02:00:42,230 --> 02:00:44,660 It's a super simple database table called books 2514 02:00:44,660 --> 02:00:46,610 with an ID column and a title column. 2515 02:00:46,610 --> 02:00:47,420 And that's it. 2516 02:00:47,420 --> 02:00:48,500 Let's see what's inside. 2517 02:00:48,500 --> 02:00:53,520 SELECT * FROM book; Not surprisingly, there are the five books. 2518 02:00:53,520 --> 02:00:56,570 So, again, you see how we're stitching these technologies together 2519 02:00:56,570 --> 02:00:58,350 using these basic building blocks. 2520 02:00:58,350 --> 02:01:01,190 So let's look now-- let me quit out of SQLite. 2521 02:01:01,190 --> 02:01:05,060 Let me shrink my terminal window and open up app.py. 2522 02:01:05,060 --> 02:01:06,540 And there's some comments in here. 2523 02:01:06,540 --> 02:01:09,260 But, for the most part, it's the same as before. 2524 02:01:09,260 --> 02:01:11,570 I'm importing some of the session stuff, so I can keep 2525 02:01:11,570 --> 02:01:13,640 track of whose shopping cart is whose. 2526 02:01:13,640 --> 02:01:17,210 I'm opening, though, in this version, the database called store.db. 2527 02:01:17,210 --> 02:01:20,210 I'm doing that same stuff, copy-paste with the session just to make sure 2528 02:01:20,210 --> 02:01:21,240 that it works. 2529 02:01:21,240 --> 02:01:23,150 And then, down here, this is where things 2530 02:01:23,150 --> 02:01:27,260 get really kind of interesting and, again, representative of web 2531 02:01:27,260 --> 02:01:28,250 apps everywhere. 2532 02:01:28,250 --> 02:01:32,660 My /route, that is, my index, has this line of code first, 2533 02:01:32,660 --> 02:01:36,440 a book's variable that gets the return value of the CS50 function-- 2534 02:01:36,440 --> 02:01:38,450 CS50 execute functions. 2535 02:01:38,450 --> 02:01:41,570 SELECT * FROM books return value. 2536 02:01:41,570 --> 02:01:42,830 What does that return? 2537 02:01:42,830 --> 02:01:45,650 Well, recall that, when using the CS50 library and you're 2538 02:01:45,650 --> 02:01:49,160 using the SQL function, this execute function within SQL, 2539 02:01:49,160 --> 02:01:55,910 you're getting back, from db.execute typically, a list of dictionaries. 2540 02:01:55,910 --> 02:01:59,510 And the keys of those dictionaries represent the columns 2541 02:01:59,510 --> 02:02:00,870 from the database table. 2542 02:02:00,870 --> 02:02:04,670 So here is a Python list per the square brackets. 2543 02:02:04,670 --> 02:02:09,920 Every element in this list is a dictionary as per the curly braces, 2544 02:02:09,920 --> 02:02:12,320 not to be confused with Jinja's curly braces today 2545 02:02:12,320 --> 02:02:14,270 and that's in the HTML files. 2546 02:02:14,270 --> 02:02:15,440 Here's one key. 2547 02:02:15,440 --> 02:02:18,540 Here's another, value and value, respectively. 2548 02:02:18,540 --> 02:02:21,320 So, again, db.execute, when using SELECT, 2549 02:02:21,320 --> 02:02:23,330 returns just a list of dictionaries. 2550 02:02:23,330 --> 02:02:25,320 It's as simple as that. 2551 02:02:25,320 --> 02:02:31,580 So if I go back to VS Code, I have a string of SQL inside of my argument 2552 02:02:31,580 --> 02:02:32,627 to this Python function. 2553 02:02:32,627 --> 02:02:34,460 That gives me a variable called books, which 2554 02:02:34,460 --> 02:02:36,420 is a list of all of the books in the database. 2555 02:02:36,420 --> 02:02:39,530 So I can certainly pass that in as an argument 2556 02:02:39,530 --> 02:02:42,835 to render template so that, when books.html is rendered, 2557 02:02:42,835 --> 02:02:44,210 it has access to all those books. 2558 02:02:44,210 --> 02:02:45,090 So let me do that. 2559 02:02:45,090 --> 02:02:48,830 Let me go into templates books.html. 2560 02:02:48,830 --> 02:02:53,570 And, perhaps not surprisingly, here's that same boilerplate as before. 2561 02:02:53,570 --> 02:02:54,930 We extend the layout. 2562 02:02:54,930 --> 02:02:56,870 Here's my body block. 2563 02:02:56,870 --> 02:03:00,360 Here's that h1 tag that just says Books at the top of the page. 2564 02:03:00,360 --> 02:03:03,470 And here is a Jinja for loop in the template that 2565 02:03:03,470 --> 02:03:07,430 is going to output, for every book, an h2, which is smaller but still bold 2566 02:03:07,430 --> 02:03:08,270 for the title. 2567 02:03:08,270 --> 02:03:13,280 And then inside of that-- or below that is a form that's identical for every 2568 02:03:13,280 --> 02:03:14,900 book except-- 2569 02:03:14,900 --> 02:03:18,860 notice that I'm very cleverly outputting a unique value 2570 02:03:18,860 --> 02:03:20,780 for every one of those forms. 2571 02:03:20,780 --> 02:03:23,390 Just relying on those primary keys back and forth, back 2572 02:03:23,390 --> 02:03:26,340 and forth to implement this notion of adding. 2573 02:03:26,340 --> 02:03:29,390 And so what is the /cart method? 2574 02:03:29,390 --> 02:03:30,710 Let's follow these breadcrumbs. 2575 02:03:30,710 --> 02:03:32,360 And I'm doing this deliberately live. 2576 02:03:32,360 --> 02:03:34,360 If you were to receive or inherit code like this 2577 02:03:34,360 --> 02:03:36,880 from a colleague, a friend, a class, you really 2578 02:03:36,880 --> 02:03:40,555 just follow these breadcrumbs to wrap your mind around what's going on. 2579 02:03:40,555 --> 02:03:42,430 And, honestly, start with the files that feel 2580 02:03:42,430 --> 02:03:46,730 the simplest like I did, like index.html, start with the entry point. 2581 02:03:46,730 --> 02:03:48,700 So let's see the cart route. 2582 02:03:48,700 --> 02:03:49,900 It's a little longer. 2583 02:03:49,900 --> 02:03:51,560 But let's walk through it step by step. 2584 02:03:51,560 --> 02:03:54,160 So the cart route supports both GET and POST. 2585 02:03:54,160 --> 02:03:57,910 It looks like, with these lines, which I haven't had occasion to use yet, 2586 02:03:57,910 --> 02:03:58,930 I'm just checking. 2587 02:03:58,930 --> 02:04:04,720 If there is no cart in the session, create a cart that's an empty list. 2588 02:04:04,720 --> 02:04:06,730 And I'm doing this logically in this program 2589 02:04:06,730 --> 02:04:11,230 because I want to make sure that the cart always exists even if it's empty. 2590 02:04:11,230 --> 02:04:14,110 Even if it's empty initially, that's what that line of code 2591 02:04:14,110 --> 02:04:15,040 is essentially doing. 2592 02:04:15,040 --> 02:04:16,760 So we can put things in it. 2593 02:04:16,760 --> 02:04:21,940 So if we have submitted something to this cart and thus the method is POST, 2594 02:04:21,940 --> 02:04:26,050 let's go ahead and grab the ID of the book from request.form. 2595 02:04:26,050 --> 02:04:31,210 Let's go ahead and make sure that that ID is actually valid, 2596 02:04:31,210 --> 02:04:33,710 and it's actually a number, so it doesn't evaluate to false. 2597 02:04:33,710 --> 02:04:35,710 And if so, do this. 2598 02:04:35,710 --> 02:04:40,210 Go into the sessions cart, which is initially an empty list. 2599 02:04:40,210 --> 02:04:43,390 And, just like we saw in week six, append an item to the list. 2600 02:04:43,390 --> 02:04:44,650 Append, append, append. 2601 02:04:44,650 --> 02:04:46,780 So we just have a list of book IDs. 2602 02:04:46,780 --> 02:04:49,960 And then, when you are done with that, redirect to the cart route. 2603 02:04:49,960 --> 02:04:52,180 Well, that's a little weird and almost recursive 2604 02:04:52,180 --> 02:04:55,510 because the cart route that we're in is redirecting to itself. 2605 02:04:55,510 --> 02:04:59,090 But redirects are always get requests when done like this. 2606 02:04:59,090 --> 02:05:02,290 So even though we ended up here via a POST, 2607 02:05:02,290 --> 02:05:06,310 this redirect is going to send me back via a GET, to the same route. 2608 02:05:06,310 --> 02:05:09,730 And that is why now, outside of the conditional, these final two lines 2609 02:05:09,730 --> 02:05:10,420 apply. 2610 02:05:10,420 --> 02:05:12,283 Here is a books variable. 2611 02:05:12,283 --> 02:05:14,950 I don't want to get all of the books because I'm not showing you 2612 02:05:14,950 --> 02:05:16,780 the catalog now of amazon.com. 2613 02:05:16,780 --> 02:05:18,460 I'm showing you your shopping cart. 2614 02:05:18,460 --> 02:05:20,030 And how do I do this? 2615 02:05:20,030 --> 02:05:27,700 I'm SELECTing * FROM books WHERE the id of the book is IN this list of ids. 2616 02:05:27,700 --> 02:05:29,530 And you haven't seen this yet most likely. 2617 02:05:29,530 --> 02:05:33,400 But, in the CS50 library, if you use parentheses and a question mark, 2618 02:05:33,400 --> 02:05:38,350 so placeholder as always, you can then plug a list into that question mark, 2619 02:05:38,350 --> 02:05:41,530 and the library will separate them all via commas. 2620 02:05:41,530 --> 02:05:43,690 You might have for a past problem set manually. 2621 02:05:43,690 --> 02:05:45,400 We will generate the commas for you. 2622 02:05:45,400 --> 02:05:47,260 So everything is nicely escaped. 2623 02:05:47,260 --> 02:05:50,990 And then we're just rendering a template cart.html, passing in those books. 2624 02:05:50,990 --> 02:05:52,060 So what's cart.html? 2625 02:05:52,060 --> 02:05:53,800 That's the last breadcrumb to follow. 2626 02:05:53,800 --> 02:05:59,170 cart.html, and really nothing going on that's that interesting here. 2627 02:05:59,170 --> 02:06:00,880 There's an h1 tag at the top. 2628 02:06:00,880 --> 02:06:04,240 There's an ordered list, which is a numbered list just because. 2629 02:06:04,240 --> 02:06:09,190 And then there's this Jinja loop that's outputting a list item or li element 2630 02:06:09,190 --> 02:06:13,670 for every book showing its title. 2631 02:06:13,670 --> 02:06:17,350 So if I go back to the store here and actually start 2632 02:06:17,350 --> 02:06:20,448 adding things to the cart, there, I've added that to the cart. 2633 02:06:20,448 --> 02:06:21,490 All right, just one book. 2634 02:06:21,490 --> 02:06:23,890 Let's jump to the last one, so the Hitchhiker's Guide to the Galaxy. 2635 02:06:23,890 --> 02:06:24,973 How about Mostly Harmless? 2636 02:06:24,973 --> 02:06:25,630 Click that. 2637 02:06:25,630 --> 02:06:27,640 Now there's two items in the shopping cart. 2638 02:06:27,640 --> 02:06:28,510 Let me go back. 2639 02:06:28,510 --> 02:06:32,200 There's now three items in the shopping cart and so forth. 2640 02:06:32,200 --> 02:06:35,300 But if I were to make this URL public-- it's currently private by default. 2641 02:06:35,300 --> 02:06:37,930 So not everyone on the internet can visit it at the same time. 2642 02:06:37,930 --> 02:06:40,030 If I were to make this public and any one of you 2643 02:06:40,030 --> 02:06:42,010 were to open it on your phone or laptop, you 2644 02:06:42,010 --> 02:06:46,030 would see an empty cart because you would have a different handstamp, 2645 02:06:46,030 --> 02:06:47,230 a different cookie. 2646 02:06:47,230 --> 02:06:50,440 But the server would be keeping track of all of our shopping carts 2647 02:06:50,440 --> 02:06:55,930 in, if you will, that flask session folder, just all sort of 2648 02:06:55,930 --> 02:06:58,460 happens automatically. 2649 02:06:58,460 --> 02:06:59,540 Phew, OK. 2650 02:06:59,540 --> 02:07:04,160 So that then is how amazon.com is implemented. 2651 02:07:04,160 --> 02:07:05,870 Questions? 2652 02:07:05,870 --> 02:07:07,043 Yeah. 2653 02:07:07,043 --> 02:07:08,210 AUDIENCE: Or is the client-- 2654 02:07:08,210 --> 02:07:12,170 so let's say if you open up this application. 2655 02:07:12,170 --> 02:07:15,898 How do the server know that this is to be a new session now? 2656 02:07:15,898 --> 02:07:20,868 [INAUDIBLE] and someone who starts up a new session. 2657 02:07:20,868 --> 02:07:25,300 How does the browser [INAUDIBLE] when someone [INAUDIBLE] that is 2658 02:07:25,300 --> 02:07:26,500 like a new session? 2659 02:07:26,500 --> 02:07:27,350 DAVID J. MALAN: A really good question. 2660 02:07:27,350 --> 02:07:28,267 How does the browser-- 2661 02:07:28,267 --> 02:07:31,270 how does the server to give you a brand-new session the first time you 2662 02:07:31,270 --> 02:07:32,290 visit a website? 2663 02:07:32,290 --> 02:07:35,380 When you visit something.com for the first time, 2664 02:07:35,380 --> 02:07:37,750 your browser will not send a cookie header. 2665 02:07:37,750 --> 02:07:39,990 There will be no cookie colon session equals value. 2666 02:07:39,990 --> 02:07:42,490 It's just going to be blank, it's going to be my showing you 2667 02:07:42,490 --> 02:07:44,023 my other hand that has no ink on it. 2668 02:07:44,023 --> 02:07:46,690 Then the server will know, well, if you didn't send me a cookie, 2669 02:07:46,690 --> 02:07:50,590 I'm going to set one for you by stamping your hand with the set cookie header. 2670 02:07:50,590 --> 02:07:53,980 It's just going to generate typically a really big random number that's 2671 02:07:53,980 --> 02:07:58,520 different for you, for me, for you to keep track of us individually. 2672 02:07:58,520 --> 02:07:59,260 So that's all. 2673 02:07:59,260 --> 02:08:03,520 It would just happen by default. And Flask makes all of that 2674 02:08:03,520 --> 02:08:07,940 happen because we have not only imported these lines at the top. 2675 02:08:07,940 --> 02:08:11,005 We have also used these configuration lines too 2676 02:08:11,005 --> 02:08:13,935 to ensure that the server does exactly what I just described. 2677 02:08:13,935 --> 02:08:16,060 If you had to do all of that manually, honestly, it 2678 02:08:16,060 --> 02:08:18,518 would be so annoying to make web applications because you'd 2679 02:08:18,518 --> 02:08:20,210 do copy-paste all of the time. 2680 02:08:20,210 --> 02:08:23,230 This is still some copy-paste, but it's way less than implementing 2681 02:08:23,230 --> 02:08:26,310 all of this cookie stuff on your own. 2682 02:08:26,310 --> 02:08:28,970 All right, how about a final set of examples 2683 02:08:28,970 --> 02:08:32,360 that allow us to escalate quickly to a larger data 2684 02:08:32,360 --> 02:08:37,610 set and make this more like an actual amazon.com or maybe an actual imdb.com. 2685 02:08:37,610 --> 02:08:40,820 And we'll do this by way of a tour of some pre-written examples 2686 02:08:40,820 --> 02:08:43,170 rather than do all of these here from scratch. 2687 02:08:43,170 --> 02:08:46,880 So I'm going to go into my terminal window. 2688 02:08:46,880 --> 02:08:50,060 I'm going to hit Control-c to kill the previous version of the store. 2689 02:08:50,060 --> 02:08:55,130 And I'm going to go into shows version 0 initially, which has our old friend 2690 02:08:55,130 --> 02:09:00,920 shows.db from our SQL lecture, which has all of the latest TV shows from IMDb 2691 02:09:00,920 --> 02:09:03,020 using the same schema as then. 2692 02:09:03,020 --> 02:09:04,700 And there's an app.py. 2693 02:09:04,700 --> 02:09:06,080 There's a requirements.txt. 2694 02:09:06,080 --> 02:09:08,120 And there's a templates folder just as predicted 2695 02:09:08,120 --> 02:09:09,560 because we're still using Flask. 2696 02:09:09,560 --> 02:09:11,690 So let's go ahead and take a look at what's 2697 02:09:11,690 --> 02:09:16,280 going on inside of this file app.py as our entry point. 2698 02:09:16,280 --> 02:09:19,130 All right, so I see some pretty familiar imports now. 2699 02:09:19,130 --> 02:09:24,420 I see shows.db using the SQL library, so nothing too interesting there. 2700 02:09:24,420 --> 02:09:26,460 The index route is super simple. 2701 02:09:26,460 --> 02:09:29,330 It's just rendering index.html. 2702 02:09:29,330 --> 02:09:30,770 And then there's a search route. 2703 02:09:30,770 --> 02:09:33,770 So this is kind of an amalgam of google.com and imdb.com. 2704 02:09:33,770 --> 02:09:35,960 I want to implement this relatively simple search 2705 02:09:35,960 --> 02:09:38,790 website for IMDb, just a search box. 2706 02:09:38,790 --> 02:09:41,960 Well, we can kind of preemptively infer how this is going to work. 2707 02:09:41,960 --> 02:09:46,400 A shows variable is being set to the return value of db.execute, 2708 02:09:46,400 --> 02:09:50,000 where I'm executing SELECT * FROM shows WHERE the title of the show 2709 02:09:50,000 --> 02:09:52,370 equals this question mark. 2710 02:09:52,370 --> 02:09:53,540 And what am I plugging in? 2711 02:09:53,540 --> 02:09:56,570 Well, just like Google, I'm plugging in the value of the q attribute 2712 02:09:56,570 --> 02:09:58,250 from the URL apparently. 2713 02:09:58,250 --> 02:10:00,920 Then I'm rendering a template called search.html. 2714 02:10:00,920 --> 02:10:05,130 And I'm passing in those shows as a list of Python dictionaries. 2715 02:10:05,130 --> 02:10:06,290 So that's it. 2716 02:10:06,290 --> 02:10:09,680 It's only-- that's the entirety of the back-end logic here. 2717 02:10:09,680 --> 02:10:10,730 It's a search engine. 2718 02:10:10,730 --> 02:10:12,600 So what might I want to do next? 2719 02:10:12,600 --> 02:10:14,037 Well, my mind goes to index.html. 2720 02:10:14,037 --> 02:10:15,870 Let's just see what the template looks like. 2721 02:10:15,870 --> 02:10:19,220 So I can wrap my mind around index.html. 2722 02:10:19,220 --> 02:10:20,700 OK, it's pretty darn simple. 2723 02:10:20,700 --> 02:10:23,710 It's just a form that has an action of /search, 2724 02:10:23,710 --> 02:10:25,550 which aligns with the route we just saw. 2725 02:10:25,550 --> 02:10:26,450 Method is get. 2726 02:10:26,450 --> 02:10:30,620 It's got an autocompleting input called q, as expected, 2727 02:10:30,620 --> 02:10:32,090 and a button called Search. 2728 02:10:32,090 --> 02:10:35,480 So not really that stimulating there versus past examples we've done. 2729 02:10:35,480 --> 02:10:39,180 Let me go into search.html, which is the other template in here. 2730 02:10:39,180 --> 02:10:43,760 So let me open my terminal and do code of templates search.html. 2731 02:10:43,760 --> 02:10:44,900 Close the terminal. 2732 02:10:44,900 --> 02:10:47,030 OK, this is kind of like the bookstore too. 2733 02:10:47,030 --> 02:10:49,910 It's just iterating over the shows, outputting list item, list item, 2734 02:10:49,910 --> 02:10:50,480 list item. 2735 02:10:50,480 --> 02:10:52,850 But I'm using an unordered list instead of ordered. 2736 02:10:52,850 --> 02:10:56,420 So there's not that much to this application to enabling search. 2737 02:10:56,420 --> 02:11:00,530 But recall that, two weeks ago, when we introduced HTML and CSS and JavaScript, 2738 02:11:00,530 --> 02:11:03,710 we completely punted to the actual google.com. 2739 02:11:03,710 --> 02:11:07,460 Today we are, if you will, google.com or imdb.com. 2740 02:11:07,460 --> 02:11:12,080 So let's go into the terminal window, do flask run. 2741 02:11:12,080 --> 02:11:14,220 I'll go back into my browser and reload. 2742 02:11:14,220 --> 02:11:15,650 So we no longer see the store. 2743 02:11:15,650 --> 02:11:17,240 We now see IMDb. 2744 02:11:17,240 --> 02:11:19,160 And it's a pretty simple search box. 2745 02:11:19,160 --> 02:11:23,840 Let me search for the office as in the past, click Search. 2746 02:11:23,840 --> 02:11:25,340 And notice two things. 2747 02:11:25,340 --> 02:11:30,290 At the top of the URL is ?q=the+office. 2748 02:11:30,290 --> 02:11:34,370 The plus is a way of encoding spaces in URLs so that it's just one long string, 2749 02:11:34,370 --> 02:11:35,383 but that's conventional. 2750 02:11:35,383 --> 02:11:36,800 And then there's a lot of offices. 2751 02:11:36,800 --> 02:11:37,730 But why is this? 2752 02:11:37,730 --> 02:11:39,530 Well, there's the American one, the UK one. 2753 02:11:39,530 --> 02:11:42,240 There's some prior ones that weren't nearly as popular as either. 2754 02:11:42,240 --> 02:11:44,810 So there's a bunch of offices in there. 2755 02:11:44,810 --> 02:11:47,390 But we could probably-- 2756 02:11:47,390 --> 02:11:49,040 we can tie our ideas together here. 2757 02:11:49,040 --> 02:11:50,450 Let me open another terminal. 2758 02:11:50,450 --> 02:11:51,470 Make it bigger. 2759 02:11:51,470 --> 02:11:54,350 Go into src9 shows0. 2760 02:11:54,350 --> 02:11:58,580 sqlite3 of shows.db .schema. 2761 02:11:58,580 --> 02:12:02,150 Oh, yeah, there's a lot of data in this database. 2762 02:12:02,150 --> 02:12:04,820 Let's do .schema show specifically. 2763 02:12:04,820 --> 02:12:06,830 OK, so we have the year of every show. 2764 02:12:06,830 --> 02:12:10,520 So this just means I can play around now with the intersection of SQL and Python 2765 02:12:10,520 --> 02:12:11,100 as follows. 2766 02:12:11,100 --> 02:12:16,010 Let me go back to how about search.html. 2767 02:12:16,010 --> 02:12:18,980 And, instead of just showing the title, why don't I 2768 02:12:18,980 --> 02:12:24,010 do something like curly curly brace show, quote, unquote, year. 2769 02:12:24,010 --> 02:12:26,080 Curly curly brace because why? 2770 02:12:26,080 --> 02:12:30,140 Well, every element in this list is a dictionary. 2771 02:12:30,140 --> 02:12:32,540 So I have access to all of those SQL columns. 2772 02:12:32,540 --> 02:12:33,590 So let me reload. 2773 02:12:33,590 --> 02:12:35,950 And now you see, oh, that's why there's so many offices. 2774 02:12:35,950 --> 02:12:37,460 They're each from different years. 2775 02:12:37,460 --> 02:12:41,050 So every piece of data you have access in SQL you have access to in Python you 2776 02:12:41,050 --> 02:12:43,150 now have access to in HTML by just knowing 2777 02:12:43,150 --> 02:12:45,100 how to stitch these ideas together. 2778 02:12:45,100 --> 02:12:47,890 All right, so what's not so good about this? 2779 02:12:47,890 --> 02:12:54,400 Well, if I go back to the search box and I search for just office, Enter, 2780 02:12:54,400 --> 02:12:59,980 there's apparently no show called literally office, at least lowercase o. 2781 02:12:59,980 --> 02:13:03,340 Let me try a little more specific, Office, as someone might type. 2782 02:13:03,340 --> 02:13:08,080 OK, so there's one version of Office, but not The Office from 2013. 2783 02:13:08,080 --> 02:13:10,870 But what if I want to have more of a wild card search? 2784 02:13:10,870 --> 02:13:12,920 Well, we can borrow these ideas too. 2785 02:13:12,920 --> 02:13:17,020 So let me go back into VS Code here in my app.py. 2786 02:13:17,020 --> 02:13:21,640 And I think here, instead of using equal, what was the keyword? 2787 02:13:21,640 --> 02:13:24,650 Yeah, so we can do like, for instance. 2788 02:13:24,650 --> 02:13:26,870 But this we have to be a little careful of. 2789 02:13:26,870 --> 02:13:29,690 We don't want to resort to a Python f-string 2790 02:13:29,690 --> 02:13:33,630 where we plug in values with percent signs and the like. 2791 02:13:33,630 --> 02:13:36,530 So this is a little bit tricky, but it's worth 2792 02:13:36,530 --> 02:13:39,800 knowing that if you want to do wildcard searches safely 2793 02:13:39,800 --> 02:13:41,940 we still should distrust the user's input. 2794 02:13:41,940 --> 02:13:43,940 So what I'm going to do is this even though it's 2795 02:13:43,940 --> 02:13:45,410 going to look a little cryptic. 2796 02:13:45,410 --> 02:13:47,240 I'm going to just do a question mark. 2797 02:13:47,240 --> 02:13:51,435 But, instead of passing in request.args.get of q, 2798 02:13:51,435 --> 02:13:54,560 first, let's tuck this in a variable so the code is a little more readable. 2799 02:13:54,560 --> 02:13:58,130 q equals that even though that's not changing anything fundamentally. 2800 02:13:58,130 --> 02:13:59,450 Let's do this. 2801 02:13:59,450 --> 02:14:03,500 Let's put my percent sign here, concatenate q with that, 2802 02:14:03,500 --> 02:14:08,150 and then put another percent sign here so that this whole string, 2803 02:14:08,150 --> 02:14:11,180 after joining and joining three things together, gets 2804 02:14:11,180 --> 02:14:13,880 plugged into the question mark and therefore escaped. 2805 02:14:13,880 --> 02:14:17,090 You should not use an f-string for this thing. 2806 02:14:17,090 --> 02:14:20,535 You should always use the question mark placeholder as we keep preaching. 2807 02:14:20,535 --> 02:14:23,660 All right, so now that I've done this, I think my search functionality just 2808 02:14:23,660 --> 02:14:24,300 got way better. 2809 02:14:24,300 --> 02:14:24,800 Why? 2810 02:14:24,800 --> 02:14:26,600 So if I go back to the form-- 2811 02:14:26,600 --> 02:14:28,550 and I'll reload to clear everything. 2812 02:14:28,550 --> 02:14:29,660 I'll zoom in a little bit. 2813 02:14:29,660 --> 02:14:33,560 Let's type in just office in lowercase with no the. 2814 02:14:33,560 --> 02:14:39,560 And I think I should get now every TV show that has office, O-F-F-I-C-E, 2815 02:14:39,560 --> 02:14:41,900 in it somewhere even if it's officer. 2816 02:14:41,900 --> 02:14:43,640 So that might not be quite what we want. 2817 02:14:43,640 --> 02:14:46,340 But there's indeed a much broader match here. 2818 02:14:46,340 --> 02:14:48,260 So more like the imdb.com. 2819 02:14:48,260 --> 02:14:49,790 But now it's a design decision. 2820 02:14:49,790 --> 02:14:52,250 Do you want your-- do you want to be really nitpicky 2821 02:14:52,250 --> 02:14:54,950 and require users to type in The Office? 2822 02:14:54,950 --> 02:14:56,390 Do you want it to be capitalized? 2823 02:14:56,390 --> 02:14:59,870 This now becomes more of a user interface or design decision, not so 2824 02:14:59,870 --> 02:15:01,250 much code. 2825 02:15:01,250 --> 02:15:03,530 All right, well, let's make one tweak here 2826 02:15:03,530 --> 02:15:06,860 that's representative all the more of today's modern apps. 2827 02:15:06,860 --> 02:15:11,540 It turns out that this approach of generating 2828 02:15:11,540 --> 02:15:15,950 new HTML every time a user submits input or visits a new URL 2829 02:15:15,950 --> 02:15:22,280 is increasingly dated whereby every URL is unique, as opposed to apps 2830 02:15:22,280 --> 02:15:23,850 being much more interactive. 2831 02:15:23,850 --> 02:15:27,770 So it turns out, there's this technique in general, 2832 02:15:27,770 --> 02:15:31,010 in the world of the web, where you use something 2833 02:15:31,010 --> 02:15:34,880 called AJAX, which used to stand for Asynchronous JavaScript And XML. 2834 02:15:34,880 --> 02:15:39,890 Nowadays, it just refers to using JavaScript 2835 02:15:39,890 --> 02:15:44,240 to get more data from the server so the user's URL doesn't even change, 2836 02:15:44,240 --> 02:15:47,540 and the whole browser screen doesn't flash as though the whole page is 2837 02:15:47,540 --> 02:15:48,560 being reloaded. 2838 02:15:48,560 --> 02:15:50,720 So this is going to look a little more cryptic. 2839 02:15:50,720 --> 02:15:55,820 But let me go ahead and show you an alternative 2840 02:15:55,820 --> 02:15:57,980 to this relatively easy approach. 2841 02:15:57,980 --> 02:16:00,175 A lot of today might be feeling like a lot. 2842 02:16:00,175 --> 02:16:02,300 It's about to feel more like a lot but not in a way 2843 02:16:02,300 --> 02:16:05,690 that you need to replicate, just to give you a taste of what more modern web 2844 02:16:05,690 --> 02:16:06,620 apps are doing. 2845 02:16:06,620 --> 02:16:08,510 I'm going to close these two tabs. 2846 02:16:08,510 --> 02:16:10,542 I'm going to go ahead and exit out of SQLite. 2847 02:16:10,542 --> 02:16:12,500 I'm going to kill my previous version of Flask. 2848 02:16:12,500 --> 02:16:15,500 And I'm going to go into shows version 2 now. 2849 02:16:15,500 --> 02:16:19,220 And, in shows2, I'm going to do flask run. 2850 02:16:19,220 --> 02:16:20,563 So the app is running. 2851 02:16:20,563 --> 02:16:22,730 I'm going to go back to my URL here and just reload. 2852 02:16:22,730 --> 02:16:25,550 And notice I've gotten rid of the Search button altogether, 2853 02:16:25,550 --> 02:16:27,000 minor aesthetic detail. 2854 02:16:27,000 --> 02:16:33,469 But what I like about this now is that if I search for O-F-F-I-C-E, 2855 02:16:33,469 --> 02:16:35,719 you're seeing the beginnings of autocomplete, 2856 02:16:35,719 --> 02:16:37,250 which is kind of everywhere. 2857 02:16:37,250 --> 02:16:41,132 But you can't implement autocomplete if you have to reload the whole page, 2858 02:16:41,132 --> 02:16:42,049 reload the whole page. 2859 02:16:42,049 --> 02:16:42,230 Why? 2860 02:16:42,230 --> 02:16:43,938 If nothing else, the user's going to be-- 2861 02:16:43,938 --> 02:16:47,010 see a big flash of white as the whole page redraws itself, 2862 02:16:47,010 --> 02:16:48,500 which is not what we're used to. 2863 02:16:48,500 --> 02:16:53,420 If I start over, O-F-F, notice the URL is not changing, nor is the whole page 2864 02:16:53,420 --> 02:16:54,139 flickering. 2865 02:16:54,139 --> 02:16:56,370 Just the URL is getting shorter, shorter, shorter. 2866 02:16:56,370 --> 02:16:58,700 And if I really go shorter, there it is. 2867 02:16:58,700 --> 02:17:02,370 Officer, now I have only this many bullets on the screen. 2868 02:17:02,370 --> 02:17:04,040 There's no more below the break. 2869 02:17:04,040 --> 02:17:06,030 So how can I do this? 2870 02:17:06,030 --> 02:17:08,090 Well, let's try to infer a little bit here 2871 02:17:08,090 --> 02:17:10,969 and be demonstrative of how you can infer 2872 02:17:10,969 --> 02:17:13,080 how a third-party websites are working. 2873 02:17:13,080 --> 02:17:16,398 If you want to-- if you want to mimic their behavior 2874 02:17:16,398 --> 02:17:18,690 or just learn better how they work-- so let me do this. 2875 02:17:18,690 --> 02:17:20,780 Let me open up developer tools. 2876 02:17:20,780 --> 02:17:22,639 Let me open the Network tab. 2877 02:17:22,639 --> 02:17:26,600 And let me search for O. And watch what happens in my Network tab 2878 02:17:26,600 --> 02:17:29,389 even though the URL of the page is not changing. 2879 02:17:29,389 --> 02:17:36,620 O. Apparently, an HTTP request was sent from my browser to this URL, 2880 02:17:36,620 --> 02:17:41,570 which is essentially representing /search?q=O. 2881 02:17:41,570 --> 02:17:44,430 Notice that the response was 200. 2882 02:17:44,430 --> 02:17:46,100 And what is in that response? 2883 02:17:46,100 --> 02:17:47,600 Well, let me click on that row. 2884 02:17:47,600 --> 02:17:50,360 Let me click on response. 2885 02:17:50,360 --> 02:17:53,209 And, very interestingly, notice what came back. 2886 02:17:53,209 --> 02:17:54,559 It's not a whole web page. 2887 02:17:54,559 --> 02:17:58,610 It's just a bunch of li elements, which you can kind of infer 2888 02:17:58,610 --> 02:18:02,490 are probably the ones that are getting snuck into the page dynamically. 2889 02:18:02,490 --> 02:18:04,620 So if I go back to the top, there's no ul here. 2890 02:18:04,620 --> 02:18:05,905 It's just a bunch of lis. 2891 02:18:05,905 --> 02:18:07,280 And watch what happens this time. 2892 02:18:07,280 --> 02:18:10,010 Let me close that panel. 2893 02:18:10,010 --> 02:18:12,559 Let me search for O-F without even hitting Enter. 2894 02:18:12,559 --> 02:18:15,090 Here we go, O-F. Now there's a second request. 2895 02:18:15,090 --> 02:18:15,590 And 2896 02:18:15,590 --> 02:18:17,990 If I zoom in, there it is, q=OF. 2897 02:18:17,990 --> 02:18:20,990 If I click on that and zoom out, you'll see a whole bunch of lis. 2898 02:18:20,990 --> 02:18:24,990 But let me claim there's fewer of them now because fewer strings match O-F. 2899 02:18:24,990 --> 02:18:29,549 And if I finally type in office, let alone the office, 2900 02:18:29,549 --> 02:18:32,129 notice now, at the very bottom of this, every time you're 2901 02:18:32,129 --> 02:18:35,520 doing an autocomplete, it's sending an HTTP request, HTTP request, 2902 02:18:35,520 --> 02:18:36,299 HTTP request. 2903 02:18:36,299 --> 02:18:38,160 Back in my day, you'd have to click Submit. 2904 02:18:38,160 --> 02:18:41,250 The whole page would reload, and you'd see the list again and again. 2905 02:18:41,250 --> 02:18:42,363 This is more modern. 2906 02:18:42,363 --> 02:18:44,280 And this is an example indeed of what's called 2907 02:18:44,280 --> 02:18:47,850 AJAX, Asynchronous JavaScript that's getting you more data 2908 02:18:47,850 --> 02:18:50,670 and slipping it into your existing web page. 2909 02:18:50,670 --> 02:18:51,930 So how does this work? 2910 02:18:51,930 --> 02:18:54,010 Let me go to VS Code here. 2911 02:18:54,010 --> 02:18:56,129 Let me open up a new terminal. 2912 02:18:56,129 --> 02:19:01,379 Let me go into src9 shows2. 2913 02:19:01,379 --> 02:19:05,129 And let me open up, for instance, the index 2914 02:19:05,129 --> 02:19:07,379 template, which is the entry point. 2915 02:19:07,379 --> 02:19:09,270 Everything at the top is sort of boring. 2916 02:19:09,270 --> 02:19:10,750 Here's the head of the page. 2917 02:19:10,750 --> 02:19:11,458 Here's the input. 2918 02:19:11,458 --> 02:19:14,000 I didn't even bother with the whole form because I'm not even 2919 02:19:14,000 --> 02:19:15,000 submitting a whole form. 2920 02:19:15,000 --> 02:19:17,740 So I don't need an action or a method or anything like that. 2921 02:19:17,740 --> 02:19:20,250 I'm just using it as a dumb input box only. 2922 02:19:20,250 --> 02:19:24,990 But notice that it is indeed an input of type search. 2923 02:19:24,990 --> 02:19:28,660 Here, now, is an empty ul, initially. 2924 02:19:28,660 --> 02:19:32,639 So this is why, when you visit the page, you see a text box but no bullets 2925 02:19:32,639 --> 02:19:33,968 because the list is empty. 2926 02:19:33,968 --> 02:19:35,010 And, in fact, watch this. 2927 02:19:35,010 --> 02:19:40,500 If I click on elements in the Developer Tools, click on body, expand the body, 2928 02:19:40,500 --> 02:19:41,549 there is the ul. 2929 02:19:41,549 --> 02:19:44,010 If I zoom in, there's nothing inside of it yet. 2930 02:19:44,010 --> 02:19:45,330 That's why we see no bullets. 2931 02:19:45,330 --> 02:19:48,597 But if I go back to the template, here's some JavaScript code, 2932 02:19:48,597 --> 02:19:50,430 which we haven't spent much time on, but you 2933 02:19:50,430 --> 02:19:53,370 can start to wrap your mind around this line by line as follows. 2934 02:19:53,370 --> 02:19:55,410 Give me a variable called input. 2935 02:19:55,410 --> 02:20:00,990 Set it equal to the element from the DOM, the tree in the computer's 2936 02:20:00,990 --> 02:20:05,250 memory for the input element, so that rectangle from our pictures 2937 02:20:05,250 --> 02:20:06,240 from last week. 2938 02:20:06,240 --> 02:20:10,590 Then listen to that input for the input event. 2939 02:20:10,590 --> 02:20:15,420 I showed briefly last week a list of like dragging and clicking and mouse up 2940 02:20:15,420 --> 02:20:17,430 and mouse down and key up and key down. 2941 02:20:17,430 --> 02:20:20,400 And input just means generally inputting into a text box. 2942 02:20:20,400 --> 02:20:21,720 Here, I'm calling a function. 2943 02:20:21,720 --> 02:20:24,693 It's an asynchronous function in the sense that there-- 2944 02:20:24,693 --> 02:20:26,360 it's going to get back to me eventually. 2945 02:20:26,360 --> 02:20:28,700 If the server is slow, it might take a moment or two. 2946 02:20:28,700 --> 02:20:32,270 But, inside of this function, give me a variable called response. 2947 02:20:32,270 --> 02:20:41,750 Go ahead and fetch the URL whose path or route is /search?q= and then 2948 02:20:41,750 --> 02:20:46,100 concatenate onto that whatever the user's input that is the value of that 2949 02:20:46,100 --> 02:20:46,940 input. 2950 02:20:46,940 --> 02:20:52,520 Then create a variable called shows, go into that response from the server, 2951 02:20:52,520 --> 02:20:54,363 and get its text value. 2952 02:20:54,363 --> 02:20:56,280 So we're not going to spend much time on this. 2953 02:20:56,280 --> 02:20:58,970 But fetch is a function that comes with browsers today 2954 02:20:58,970 --> 02:21:02,360 in JavaScript that lets you make an HTTP request. 2955 02:21:02,360 --> 02:21:04,130 Fetch more content from the server. 2956 02:21:04,130 --> 02:21:08,420 And you essentially pass it in the URL that you want to fetch or get. 2957 02:21:08,420 --> 02:21:11,720 This function here response.text just grabs the text 2958 02:21:11,720 --> 02:21:16,460 from that response, which means if I go to the Network tab and I type in O 2959 02:21:16,460 --> 02:21:21,260 as before and I click this, response.text is all of this stuff 2960 02:21:21,260 --> 02:21:22,950 that we manually looked at earlier. 2961 02:21:22,950 --> 02:21:26,033 So you're just getting all of the text that came back from the server that 2962 02:21:26,033 --> 02:21:27,600 happened to be lis. 2963 02:21:27,600 --> 02:21:33,150 Lastly, go into the document, the DOM, select the ul element, 2964 02:21:33,150 --> 02:21:40,980 go into its inner HTML, and change the inner HTML to be that text, aka shows. 2965 02:21:40,980 --> 02:21:43,950 And so what's happening here-- and watch this now when I-- let's 2966 02:21:43,950 --> 02:21:45,430 reload the page. 2967 02:21:45,430 --> 02:21:47,970 Let me show you the Elements tab. 2968 02:21:47,970 --> 02:21:51,092 Let me highlight and zoom in on the ul element. 2969 02:21:51,092 --> 02:21:53,550 In the bottom of the screen, nothing's there yet but watch. 2970 02:21:53,550 --> 02:21:57,360 As soon as I type O, and I get back all of those lis, 2971 02:21:57,360 --> 02:22:02,790 and they get crammed into the inner HTML of the ul, watch the ul as I type O, 2972 02:22:02,790 --> 02:22:04,560 there's now more stuff there. 2973 02:22:04,560 --> 02:22:08,340 And, indeed, if I expand it, there are all of the lis. 2974 02:22:08,340 --> 02:22:11,440 That is the inner HTML that just got automatically populated. 2975 02:22:11,440 --> 02:22:16,810 And if I type in more, officer, notice that if I scroll down, 2976 02:22:16,810 --> 02:22:18,490 there's only so many of them. 2977 02:22:18,490 --> 02:22:22,020 And if I just type in nonsense, notice it's back to 0 2978 02:22:22,020 --> 02:22:25,390 because there's no text coming back from the server. 2979 02:22:25,390 --> 02:22:28,320 This then is an example of the beginning of an API. 2980 02:22:28,320 --> 02:22:31,510 And I've used this term over time, but an API is an Application Programming 2981 02:22:31,510 --> 02:22:32,010 Interface. 2982 02:22:32,010 --> 02:22:35,280 And it's a standardized way, generally, of getting data 2983 02:22:35,280 --> 02:22:39,070 from someone else's server or service into your application. 2984 02:22:39,070 --> 02:22:42,300 Now, in this contrived scenario, I am both the web developer, 2985 02:22:42,300 --> 02:22:44,430 and I'm the author of the API. 2986 02:22:44,430 --> 02:22:48,540 But they're implementing-- they're being implemented in the same application. 2987 02:22:48,540 --> 02:22:52,530 But you could imagine, actually, querying amazon.com for actual books 2988 02:22:52,530 --> 02:22:55,740 and actual prices and actual photographs thereof 2989 02:22:55,740 --> 02:22:57,850 from their servers instead of your own. 2990 02:22:57,850 --> 02:23:00,420 And so they might very well send to your server 2991 02:23:00,420 --> 02:23:05,010 those kinds of responses by just sending you a whole bunch of text that, again, 2992 02:23:05,010 --> 02:23:07,090 might just look like all of these lis. 2993 02:23:07,090 --> 02:23:10,920 But the lis is a sloppy way of sending information. 2994 02:23:10,920 --> 02:23:15,120 Nowadays, it's not common to use HTML, nor something called XML 2995 02:23:15,120 --> 02:23:16,680 to send back your data. 2996 02:23:16,680 --> 02:23:19,920 Rather, it's more common to get back something 2997 02:23:19,920 --> 02:23:23,230 called JSON, JavaScript Object Notation. 2998 02:23:23,230 --> 02:23:26,440 And odds are, for your final project, if you do anything with an API, 2999 02:23:26,440 --> 02:23:28,270 you'll encounter this in the real world. 3000 02:23:28,270 --> 02:23:34,780 And JSON looks very, very similar to what a dictionary and a list 3001 02:23:34,780 --> 02:23:36,070 looks like in Python. 3002 02:23:36,070 --> 02:23:38,320 The syntax is almost the same as you've seen. 3003 02:23:38,320 --> 02:23:41,470 However, in the world of JSON, JavaScript Object Notation, 3004 02:23:41,470 --> 02:23:44,920 you have to use double quotes around your strings. 3005 02:23:44,920 --> 02:23:46,840 You cannot use single quotes. 3006 02:23:46,840 --> 02:23:48,920 And, generally, it might look like this. 3007 02:23:48,920 --> 02:23:52,690 So here, for instance, is a response from a more modern version 3008 02:23:52,690 --> 02:23:57,250 of a server that's not sending back li tags and a messy bunch of HTML. 3009 02:23:57,250 --> 02:23:59,950 It's sending back something that's more machine readable. 3010 02:23:59,950 --> 02:24:04,822 So this text, ugly as it is, is much easier for a JSON parser, a function 3011 02:24:04,822 --> 02:24:06,280 to read, because it's all standard. 3012 02:24:06,280 --> 02:24:09,260 It's all square brackets, curly braces, quotes, colons, and all of that. 3013 02:24:09,260 --> 02:24:11,427 And, even though it looks like a mess on the screen, 3014 02:24:11,427 --> 02:24:14,380 it's very standardized, unlike li tags which 3015 02:24:14,380 --> 02:24:17,320 who knows what might come back in those aesthetically? 3016 02:24:17,320 --> 02:24:20,080 Since there's so many HTML tags, not to mention CSS. 3017 02:24:20,080 --> 02:24:22,100 So let's make a change here. 3018 02:24:22,100 --> 02:24:24,470 Let me go back to VS Code here. 3019 02:24:24,470 --> 02:24:29,260 Let me close this tab and quit out of this version of my application. 3020 02:24:29,260 --> 02:24:34,120 And let me show one final version of shows by going into shows version 3. 3021 02:24:34,120 --> 02:24:38,000 And, in this version, I'm going to go ahead and do this. 3022 02:24:38,000 --> 02:24:41,050 I'm going to open up app.py. 3023 02:24:41,050 --> 02:24:43,930 And, in app.py, I'm going to import one more function 3024 02:24:43,930 --> 02:24:47,950 from the flask framework called jsonify, which is not necessarily 3025 02:24:47,950 --> 02:24:48,770 a technical term. 3026 02:24:48,770 --> 02:24:50,980 It just means turn something into JSON. 3027 02:24:50,980 --> 02:24:52,340 So what does that mean? 3028 02:24:52,340 --> 02:24:57,105 Well, notice that, down here, I have a search route that looks like this. 3029 02:24:57,105 --> 02:24:59,980 But, before we look at that, which is going to end with this spoiler, 3030 02:24:59,980 --> 02:25:02,330 like jsonify, let me actually do this. 3031 02:25:02,330 --> 02:25:05,740 Let me open up also from shows version 2, 3032 02:25:05,740 --> 02:25:09,700 the previous version of this, which looked like this here. 3033 02:25:09,700 --> 02:25:10,990 This was the app-- 3034 02:25:10,990 --> 02:25:13,210 the search route from the previous example. 3035 02:25:13,210 --> 02:25:14,740 Here's how I got q. 3036 02:25:14,740 --> 02:25:19,300 Here's how again I did the escaping of the user's input with the wildcard. 3037 02:25:19,300 --> 02:25:20,830 But notice that I also did this. 3038 02:25:20,830 --> 02:25:25,080 If the user got back no results, then I just gave it an empty list instead. 3039 02:25:25,080 --> 02:25:29,580 But let me show you two from last time. search.html looked like this. 3040 02:25:29,580 --> 02:25:32,280 And shows2 in templates, shows.html-- 3041 02:25:32,280 --> 02:25:34,320 whoops. 3042 02:25:34,320 --> 02:25:39,510 search.html, what you'll see here is the very simple template 3043 02:25:39,510 --> 02:25:43,230 that generated all of that text, all of those lis just again 3044 02:25:43,230 --> 02:25:44,240 and again and again. 3045 02:25:44,240 --> 02:25:45,990 So we're going to get rid of that template 3046 02:25:45,990 --> 02:25:48,280 in this third and final version here. 3047 02:25:48,280 --> 02:25:52,380 So if I go into shows3, notice I'm doing the same code as before. 3048 02:25:52,380 --> 02:25:57,150 I'm building up a Python list of shows that results from that SQL query. 3049 02:25:57,150 --> 02:25:59,640 But, instead of passing it into any template, 3050 02:25:59,640 --> 02:26:02,670 I'm just jsonifying that text. 3051 02:26:02,670 --> 02:26:04,620 What that means is that what I'm essentially 3052 02:26:04,620 --> 02:26:06,720 going to send from the server to the browser 3053 02:26:06,720 --> 02:26:10,300 is literally something that looks like this here on the screen. 3054 02:26:10,300 --> 02:26:16,350 So if I go back to VS Code here and run flask run in this final version, 3055 02:26:16,350 --> 02:26:18,922 and I go over to my other tab here and reload, 3056 02:26:18,922 --> 02:26:20,380 I have the same response as before. 3057 02:26:20,380 --> 02:26:22,088 And it's actually going to work the same. 3058 02:26:22,088 --> 02:26:23,920 Office is going to still work the same. 3059 02:26:23,920 --> 02:26:30,040 But if I undo that and go to inspect and go to my Network tab here 3060 02:26:30,040 --> 02:26:33,520 and now search for O, same paradigm as before. 3061 02:26:33,520 --> 02:26:36,830 There /search?q=o, let's click on that. 3062 02:26:36,830 --> 02:26:39,640 But notice-- and let me make this even bigger on the screen-- 3063 02:26:39,640 --> 02:26:45,430 this, even though it looks more verbose and it is, it's way more standardized. 3064 02:26:45,430 --> 02:26:49,300 And this is the, quote, unquote, "right way" to send data over the internet 3065 02:26:49,300 --> 02:26:53,300 when you want two pieces of software to generate and consume it respectively. 3066 02:26:53,300 --> 02:26:56,040 Here, now, we have not only just the titles of the shows, 3067 02:26:56,040 --> 02:26:58,540 but I've even been generous and sent the number of episodes, 3068 02:26:58,540 --> 02:27:00,730 the unique ID, the title, the year, I've essentially 3069 02:27:00,730 --> 02:27:05,350 sent the entire SQL database in a standard, portable, textual format 3070 02:27:05,350 --> 02:27:07,190 that my code can now use. 3071 02:27:07,190 --> 02:27:10,450 So, when you think about most any modern application, we come back to Gmail. 3072 02:27:10,450 --> 02:27:14,020 When you access something like Gmail in your browser 3073 02:27:14,020 --> 02:27:17,620 and you are waiting and waiting and waiting in a new email comes in, 3074 02:27:17,620 --> 02:27:18,490 what has happened? 3075 02:27:18,490 --> 02:27:20,770 Tonight, for instance, open up the Inspector. 3076 02:27:20,770 --> 02:27:21,850 Watch the Network tab. 3077 02:27:21,850 --> 02:27:24,150 And, odds are, every few seconds, every few minutes, 3078 02:27:24,150 --> 02:27:28,100 you'll see some kind of response coming from the server, maybe JSON, maybe 3079 02:27:28,100 --> 02:27:29,940 some other format, containing your email, 3080 02:27:29,940 --> 02:27:32,120 including at least its subject line because that's 3081 02:27:32,120 --> 02:27:34,940 what's adding more and more HTML to the browser. 3082 02:27:34,940 --> 02:27:37,550 Indeed, if you open up the Elements tab and just watch it 3083 02:27:37,550 --> 02:27:40,580 in the most watching-paint-dry kind of situation, 3084 02:27:40,580 --> 02:27:44,990 you'll see probably more and more emails appear in the underlying DOM 3085 02:27:44,990 --> 02:27:47,400 or document object model inside of the browser. 3086 02:27:47,400 --> 02:27:49,730 So I can't stress enough that, even though we've 3087 02:27:49,730 --> 02:27:53,330 spent just a few weeks on SQL and the HTML, CSS, and JavaScript 3088 02:27:53,330 --> 02:27:57,200 and now Flask together, that is how the web works. 3089 02:27:57,200 --> 02:27:59,840 That is how all of today's modern apps work and, hopefully, 3090 02:27:59,840 --> 02:28:01,310 you too with your final projects. 3091 02:28:01,310 --> 02:28:02,250 That's it for today. 3092 02:28:02,250 --> 02:28:04,850 We will see you one last time next week for the end of CS50. 3093 02:28:04,850 --> 02:28:06,950 [APPLAUSE] 3094 02:28:06,950 --> 02:28:08,450 3095 02:28:08,450 --> 02:28:11,800 [MUSIC PLAYING] 3096 02:28:11,800 --> 02:28:37,000