CS50 Video Player
    • 🧁

    • 🍬

    • 🍉

    • 🍿
    • 0:00:02YULIIA ZHUKOVETS: Hi, everyone and welcome to CS50 Week 9 section.
    • 0:00:06My name is Yuliia, and I'm a preceptor here on Harvard's campus,
    • 0:00:10helping with all things CS50 on college and online.
    • 0:00:14And today is going to be our last section.
    • 0:00:17We're going to talk about web development and [INAUDIBLE]
    • 0:00:20and really just put all of the things together, Python, SQL, HTML, and now
    • 0:00:25Flask that we've learned this week, to create our own websites.
    • 0:00:30And to take a peek of what's coming ahead,
    • 0:00:32here is our agenda, which consists of quite a few things.
    • 0:00:35But hopefully breaking it down will help you understand better
    • 0:00:40how we can structure our websites.
    • 0:00:43And contrary of what we did last week where we used just HTML and CSS,
    • 0:00:48maybe a little bit of JavaScript, we're going
    • 0:00:50to make these websites even more dynamic, even more interactive with even
    • 0:00:55more features that we saw last week.
    • 0:00:58And before we get started, let's talk about our example of today's section.
    • 0:01:03So today we're going to work on the birthdays website.
    • 0:01:07And the end goal might look something like this,
    • 0:01:11where we want to input some birthdays, for example, birthdays of your friends.
    • 0:01:17And then on the bottom, we're going to display
    • 0:01:19a table of all the inputted birthdays in a nicely formatted way.
    • 0:01:25And in just a second, we're going to take a little tour of what's
    • 0:01:27already in there.
    • 0:01:28But most of these functionalities we'll implement ourselves together today.
    • 0:01:34And let's talk a little bit about what goes behind these websites, specifically
    • 0:01:40birthdays or in finance, which is one of the problems in this week's problem set
    • 0:01:44that you will jump into.
    • 0:01:46And it really is anchored on the model of MVC, Model View Controller, whereby
    • 0:01:52view is sort of what you see as a user, the visual aspect of the website.
    • 0:01:58So the HTML files themselves, the CSS stylistics, so the view of the website
    • 0:02:06itself.
    • 0:02:07The second part, the controller, is this intermediary guide that helps actually
    • 0:02:12have this interactive part between, OK, this is what the user sees, the view,
    • 0:02:17and the model itself.
    • 0:02:19So the controller would be, for example, submitting, adding the birthday,
    • 0:02:27having this interaction in our application.
    • 0:02:30And then lastly, the model is usually where we would store our data.
    • 0:02:36So something that exists on the back end that user doesn't necessarily
    • 0:02:40have access to.
    • 0:02:41And that would be our database in this case.
    • 0:02:44So having this three way relationship of the view
    • 0:02:49being something that the user sees online when they open the web page.
    • 0:02:55Controller is the intermediary part that connects the two things together.
    • 0:03:00And this is what we usually see in app.py.
    • 0:03:03So the one where we render our templates, where we
    • 0:03:06query the database, where we grab the information from the page.
    • 0:03:10So this is where everything sort of comes together.
    • 0:03:13And then the model is usually like the backend,
    • 0:03:16that database of some logic that is not necessarily like available to user
    • 0:03:20to just look at, but obviously it is a fundamental or foundational part
    • 0:03:26of the website.
    • 0:03:27And now looking a little bit ahead, this is
    • 0:03:30how our birthdays folder's structured.
    • 0:03:33So this would be a quite common setup if you're using the MVC model whereby
    • 0:03:40static is where you would usually put stylesheets or even images,
    • 0:03:47like something that doesn't necessarily change.
    • 0:03:49Hence the word static.
    • 0:03:51Templates is where you will store your HTML pages.
    • 0:03:56So in this case, we only have one, index.html.
    • 0:04:00But if you're creating more of them, template
    • 0:04:02is the folder where they would go into.
    • 0:04:04App.py is that controller piece.
    • 0:04:06That pieces everything together and makes all of these things interactive.
    • 0:04:11And then birthdays.db or whatever other database you have
    • 0:04:16is the last component here.
    • 0:04:19But before we keep moving, let's talk a little bit
    • 0:04:24about what parts of this folder exactly attributes
    • 0:04:27to what part of the MVC model.
    • 0:04:29For example, which parts of this birthday folder
    • 0:04:34sort of attribute to the view component of the model or I guess the framework?
    • 0:04:42So the view component.
    • 0:04:45Which files or even folders in this setup help us create the view part?
    • 0:04:51Yeah, so index.html and styles.css.
    • 0:04:55Those visual pieces that the user sees on the website or obviously HTML
    • 0:05:01is where we render everything, what the user able to see.
    • 0:05:04Styles.css really helps us with this visual component, making things pretty,
    • 0:05:08making things look nicer.
    • 0:05:11What about controller?
    • 0:05:12And I know I'll gave it up a little bit already,
    • 0:05:14so this should be an easy guess.
    • 0:05:17But what about controller?
    • 0:05:19Which part of this folder setup relates to it.
    • 0:05:22App.py.
    • 0:05:22Exactly.
    • 0:05:23So app stands for Application.
    • 0:05:25And this is where we write our logic, our back end, where
    • 0:05:30we're creating all the necessary steps of grabbing the data, maybe
    • 0:05:34from the user, processing it in some way, maybe looking into our database,
    • 0:05:38and then outputting something to view.
    • 0:05:41And then lastly, if we're talking about the model.
    • 0:05:45So I mentioned briefly that that's data related or maybe something that
    • 0:05:50exists on the back back end and the user might not necessarily
    • 0:05:55have direct access to.
    • 0:06:00BIrthdays.db.
    • 0:06:01Yeah, exactly.
    • 0:06:03So birthdays.db is where we store our information.
    • 0:06:07And DB stands for Database.
    • 0:06:10And let's take a little peek into what we have here.
    • 0:06:16So on the first slide, we saw how the birthdays website
    • 0:06:21should look like in the end.
    • 0:06:23This is what we have so far.
    • 0:06:26So pretty bare bones.
    • 0:06:28It just has the sections, the components that we need to fill out ourselves.
    • 0:06:34But let me create a new terminal.
    • 0:06:38But if we take a look into each of the files, for example.
    • 0:06:51App.py.
    • 0:06:52It's something similar to what you might have seen during lecture when David was
    • 0:06:57talking about one of the applications.
    • 0:06:59And we're going to wave our hand at all of the things that come beforehand.
    • 0:07:03And I'm just going to say that this is really helpful for our website to run.
    • 0:07:06But paying attention to the more juicy part,
    • 0:07:10this is where we're going to write all of our functionality.
    • 0:07:14So some things already exist here.
    • 0:07:16For example, defining a route.
    • 0:07:18And we're going to very briefly talk about what it is.
    • 0:07:22The methods that this route will take.
    • 0:07:26And then some commented to do's that it will be up to us to implement.
    • 0:07:34And then let's take a peek at index.html.
    • 0:07:44What is it called?
    • 0:07:48Let me delete this guy then.
    • 0:07:51So I had to CD into templates first before I did code index.html.
    • 0:07:57So here already too we have some pre-populated web pages,
    • 0:08:01since we already saw this bare bones part.
    • 0:08:04So there must be something already in the HTML.
    • 0:08:07And again, we're not going to go line by line to talk
    • 0:08:11about what each part represents.
    • 0:08:13But you see some familiar things.
    • 0:08:16For example, the reference of some specific styles,
    • 0:08:19the local styles sheet, and then already some divisions or containers that
    • 0:08:26were created for us as well as the table.
    • 0:08:29So let's go back and let's start talking about even more
    • 0:08:33details that go into this.
    • 0:08:34This is still waving our hand and there's the controller
    • 0:08:37and there's the view and the model, and this is how the folder looks like.
    • 0:08:40But how exactly do we put all of these things together?
    • 0:08:43Well, let's start talking about routes.
    • 0:08:46And this is something that you probably see in the day to day life as well.
    • 0:08:53If maybe google.com is the general website, after that you see
    • 0:08:58slash and then it routes you to specific things.
    • 0:09:00Or similarly, pretty much on any website you would go to now,
    • 0:09:03there is the main domain and then the specific route that
    • 0:09:09allows you to access those web pages.
    • 0:09:11So for example, if we were to have a birthdays.net domain for our website,
    • 0:09:18the slash route would just be the main page.
    • 0:09:21And that's very common as well to just have slash represent the main page.
    • 0:09:25So the landing page where the user would go to in the first place.
    • 0:09:30But maybe there is a second page with a route add.
    • 0:09:34And those have completely different functionalities.
    • 0:09:37In the add, perhaps we would want to add a birthday
    • 0:09:40or maybe even add a friend to our friend group list.
    • 0:09:45And the way that we can define what exactly
    • 0:09:49happens when the user encounters that route is by having such syntax.
    • 0:09:55So the first line would be defining that specific route.
    • 0:10:00So for example, slash or slash add, wherever you want your user to redirect.
    • 0:10:06And then after that is where you're defining the function itself.
    • 0:10:09So the second part of it is really something that we've been familiar with.
    • 0:10:13We've been creating our own functions.
    • 0:10:15You can think about it as almost writing a helper function, a very specific one
    • 0:10:21that would pertain just for that web page.
    • 0:10:25And so in this case, you can just define it
    • 0:10:27as index is its commonly given name to the main pages.
    • 0:10:32And then after that, you just might want to return the index page.
    • 0:10:36And the function for that would be render template.
    • 0:10:40And they all really come from this Flask micro library.
    • 0:10:44So if you're curious, do I have to write render template yourself?
    • 0:10:49No, you don't.
    • 0:10:49Someone already took care of it, and that's
    • 0:10:51why we are using Flask micro libraries as a stepping stone.
    • 0:10:55We're using all the things that were pre-made before for us.
    • 0:10:59So for example, if we just wanted to have a plain main page,
    • 0:11:03for example, something is already written in index.html
    • 0:11:06and we just want to render it.
    • 0:11:08This would be a quite simple setup to achieve that.
    • 0:11:16And then lastly, of course, you're familiar with the lecture.
    • 0:11:20We use flask run to actually see that website come to life.
    • 0:11:25So similar to HTTP server where it was just a local host
    • 0:11:29and then we're able to see it on a different web page,
    • 0:11:33flask run just creates this temporary link
    • 0:11:37that you can access and see your app.
    • 0:11:43But there's another part.
    • 0:11:45And again, it was sort of there but hidden.
    • 0:11:48Request methods.
    • 0:11:49So if we go back to this slide again, what request method
    • 0:11:56does this route take?
    • 0:12:04What is the method here?
    • 0:12:10Yeah, exactly.
    • 0:12:11But we don't specify that.
    • 0:12:13And the reason for that is because get is the default.
    • 0:12:16So if you're just wanting to render the web page or display something,
    • 0:12:21you're not necessarily submitting anything,
    • 0:12:23you don't need to specify get explicitly,
    • 0:12:26because that would be a default.
    • 0:12:28But if your page is both for rendering and displaying something and submitting
    • 0:12:35some data, it is important to specify that it can be both the get
    • 0:12:40and the post.
    • 0:12:41And just to get a little bit of practice,
    • 0:12:43let's consider these different scenarios and think about which request method
    • 0:12:49we would pick in each scenario.
    • 0:12:51So for example, if the user goes to a website and lands on the login page,
    • 0:12:58would that be a get or a post?
    • 0:13:03So I'm just typing, for example, birthdays.net/login enter.
    • 0:13:09Yeah, exactly.
    • 0:13:10That would be a get, because we are just landing on the web page.
    • 0:13:15We haven't even submitted anything.
    • 0:13:17We just maybe typed the URL or navigated from somewhere in Google.
    • 0:13:21And we're just rendering the web page.
    • 0:13:23I want to see just the login HTML rendered in front of me.
    • 0:13:29But if a user enters their username and password and clicks Login,
    • 0:13:35is that get or post?
    • 0:13:39Post.
    • 0:13:40Exactly.
    • 0:13:41So this is sort of like the second step of that scenario.
    • 0:13:44I navigated to birthdays.net/login.
    • 0:13:47I saw the rendered web page in front of me.
    • 0:13:50But now I have typed my username and my password, and I clicked the button.
    • 0:13:54So I'm sending some information back to my app.py
    • 0:13:59so that maybe I can go into the database and check is the password correct
    • 0:14:04and does the user exist.
    • 0:14:06So any time when you have this submit action or some grabbing information
    • 0:14:12from the user and giving it to app.py, you want to use post.
    • 0:14:17And while you can still use get, post just adds this layer of security
    • 0:14:22to your information, because we wouldn't want your username or password
    • 0:14:26to be public.
    • 0:14:28But what about user submitting its birthday?
    • 0:14:33And I'm going to talk a little bit less here to give you some time to guess.
    • 0:14:38But if a user submits a birthday, is that post or get?
    • 0:14:45Post.
    • 0:14:45Exactly.
    • 0:14:46So again, a very good keyword to look out for,
    • 0:14:49even if you're thinking through the design of your web app.
    • 0:14:54If you're saying, OK, on x page, I want my user to submit y.
    • 0:15:01Submit is your keyword.
    • 0:15:03That's post.
    • 0:15:04And lastly, if a user navigates to the main page of the birthdays website,
    • 0:15:09would that be get or post?
    • 0:15:16Get.
    • 0:15:17Exactly.
    • 0:15:19Perfect.
    • 0:15:19So now that we talked a little bit about that, let me take a pause and ask,
    • 0:15:27what questions do we have?
    • 0:15:41Yeah.
    • 0:15:41So if we're using get-- so someone asked,
    • 0:15:45would get in the form mean that the inputs are in the URL?
    • 0:15:48Yes, so if we're using get as our method for the form,
    • 0:15:52that would mean that whatever inputs you have will show up in the URL.
    • 0:15:55And as David mentioned in the lecture, that's
    • 0:15:57not ideal, especially for sensitive information like login,
    • 0:16:02password, and username.
    • 0:16:04I think I saw some questions above as well.
    • 0:16:22What other questions do we have about--
    • 0:16:28yeah, so post requests are more secure than get requests.
    • 0:16:31Exactly.
    • 0:16:31And for the reason that we don't see the information
    • 0:16:34that the user inputs in the URL.
    • 0:16:39All right.
    • 0:16:39Let's move on and talk about forms.
    • 0:16:44So far we've been talking about submit, submitting the information.
    • 0:16:48But how exactly can we grab that information from the user?
    • 0:16:52So this is jumping to the HTML side of things,
    • 0:16:57because we're thinking about the view part of the framework.
    • 0:17:02So whatever the user sees and that attributes to the HTML files.
    • 0:17:06So now we're going to talk about that.
    • 0:17:09And as the name suggests, the tag for the form would be form.
    • 0:17:13And then inside of it, you can specify the features of it, the attributes.
    • 0:17:19So the action is where we would specify the route to request.
    • 0:17:26So what do we want to navigate to?
    • 0:17:31And then the method is where we specify get or post.
    • 0:17:34And as we talked, it could also be get, but post would be preferred.
    • 0:17:38And then we just set up this sort of container you can always imagine,
    • 0:17:43but there is nothing inside of the form yet.
    • 0:17:45It's currently blank.
    • 0:17:47So we also need to specify the inputs, the specific empty boxes where the user
    • 0:17:54can actually type in the information.
    • 0:17:56And in fact, it can be multiple ones.
    • 0:17:59It doesn't have to be just one.
    • 0:18:00So for example, to replicate the birthdays website,
    • 0:18:05we might want to do something like this where the first three
    • 0:18:09elements are the input elements specifying friend's name, month,
    • 0:18:14and day.
    • 0:18:15And notice that the type is also specified in each of the tags.
    • 0:18:21So the first one is text, because we want to get the name of our friend.
    • 0:18:25And then the second and the third are numbers,
    • 0:18:28because we want to make sure that the user only
    • 0:18:31inputs the numbers for month and day.
    • 0:18:33You could try to accommodate for month being typed in as a word,
    • 0:18:39but we'll simplify things a little bit.
    • 0:18:42And then lastly, we have our button submit.
    • 0:18:46So in between the two tags, you see the Submit word.
    • 0:18:50And that would specify what you see displayed over there.
    • 0:18:55But the type submit is different in a way that is just
    • 0:18:58submits the form when it's clicked.
    • 0:19:01So in short, this is the part that can be changed,
    • 0:19:05but the type must be submit, because we want
    • 0:19:08to make sure it gets back to app.py.
    • 0:19:13So now that we know a little bit more and we've
    • 0:19:17talked about forms and the routes, let's start changing some things.
    • 0:19:22So let's go to our cs50.dev.
    • 0:19:28And let's add the form itself to the HTML file.
    • 0:19:35So we already very conveniently have a to do here.
    • 0:19:39And on the slide just now, we saw pretty much
    • 0:19:43the outline of what it would look like.
    • 0:19:46So first let's create a form tag.
    • 0:19:51And inside of the tag, we want to specify the action, which
    • 0:19:59would just be the slash.
    • 0:20:01And then the method being post, because we want to ensure that it's secure.
    • 0:20:10And then after that, we're just going to have a bunch of input tags
    • 0:20:14where the user would actually input the information.
    • 0:20:19So the first one would be for the name.
    • 0:20:25And let's call it friend, just so that it's not name
    • 0:20:28equals name, which is a little confusing.
    • 0:20:29The type of it is text, because we want to make sure
    • 0:20:32that the user is typing in the word.
    • 0:20:36Next would be month.
    • 0:20:42And this is just by American standards where a month comes before the day.
    • 0:20:46But if you're doing it on your own, definitely feel free to flip it.
    • 0:20:49Whatever makes the most sense to you.
    • 0:20:52With the type being number, because we want to make sure, again, that the user
    • 0:20:57is inputting the correct information.
    • 0:21:00And then the last one is also number but for the day.
    • 0:21:09And finally, we have the Submit button.
    • 0:21:19That will help us actually send.
    • 0:21:28Birthday.
    • 0:21:29The information back to app.py.
    • 0:21:32Let me save this.
    • 0:21:34And let me reload.
    • 0:21:37And now I see the input fields and I also see my Add Birthday button.
    • 0:21:44Now it's also as an input.
    • 0:21:45So let me change it to button.
    • 0:22:09Every time I test something and it works,
    • 0:22:11and then when I actually need to show it to you guys, it doesn't.
    • 0:22:15But here it is.
    • 0:22:16So three input fields and then the button that will help us
    • 0:22:21submit the birthday to our database.
    • 0:22:25But I argue we can make things a little bit more intuitive for the user as well.
    • 0:22:29They don't really know what they were supposed to put where.
    • 0:22:32So we can explore another tag that's called placeholder.
    • 0:22:36So this would be that gray text that sometimes shows up
    • 0:22:40in the text fields when you try to input something on the website.
    • 0:22:44So for that, we can specify that too.
    • 0:22:46So for example, name, placeholder, month, and then placeholder day.
    • 0:23:00And that should give the user a bit more intuition
    • 0:23:03as to what is expected of them.
    • 0:23:06So, for example, my name is Yuliia.
    • 0:23:11And then Add Birthday.
    • 0:23:14But nothing is happening so far, because we haven't really updated the database.
    • 0:23:20We haven't connected things to each other.
    • 0:23:21So right now I'm just clicking and it goes somewhere,
    • 0:23:24but not really getting recorded.
    • 0:23:29But what other things we can consider here?
    • 0:23:33For example, if I type Yuliia, that works.
    • 0:23:38Now, notice when I try to type letters, and you can't see on my keyboard,
    • 0:23:42but I'm actually trying to hit some letters, it's not inputting.
    • 0:23:46When I try numbers, it works.
    • 0:23:48But what if I accidentally want to type months as December being 12
    • 0:23:53and then I accidentally click 123?
    • 0:23:58That's not right.
    • 0:23:59There is no month 123.
    • 0:24:02It stops at 12.
    • 0:24:03But it's still letting me submit it.
    • 0:24:05So for that too, there are some guardrails that we can implement.
    • 0:24:09And specifically inside of the tags, we can specify the min and the max values.
    • 0:24:14So for month, the min would be 1 and then the maximum would be 12.
    • 0:24:23Similarly, for day, the minimum is 1 and then the maximum is 31.
    • 0:24:37So let us save it here.
    • 0:24:39Let's go back to the web page.
    • 0:24:42Yuliia is fine.
    • 0:24:44But here when I try to type to whatever, it lets me type it,
    • 0:24:50but then it gives me a warning that it must be less than or equal to 12.
    • 0:24:57Similarly, if I try to do negative 6 and I try to submit it, it doesn't work.
    • 0:25:03And you can get a little bit nitpicky and you say, well,
    • 0:25:07if the month is February, the max is 28 and then not each month has 31 days.
    • 0:25:14But those are all the small details that you can think about later.
    • 0:25:18But this is the bare bones guardrails that would prevent the user from typing,
    • 0:25:23for example, day 123 or some month that doesn't exist.
    • 0:25:30OK, so now we have some part of the website implemented.
    • 0:25:34And let's go back to our slides and keep exploring it even further.
    • 0:25:40So now that we updated the view, we have the visual part
    • 0:25:46on the website signaling to the user where
    • 0:25:49they need to input the information, what exactly they need to input.
    • 0:25:53Now we need to implement this interactive component,
    • 0:25:56updating our model and our controller so that the two actually
    • 0:26:01interact between each other.
    • 0:26:03So in terms of updating the model, how do we
    • 0:26:06get the information from the website.
    • 0:26:08We input it on the HTML, but where does it go?
    • 0:26:11And this is sort of like, again, where we can wave our hands and say,
    • 0:26:16this is how we all make it work.
    • 0:26:20So notice that when I was creating my input tags,
    • 0:26:24I specified a name for each of them.
    • 0:26:27So for example, for the name field, it's friend.
    • 0:26:30For the months field, it's month, and for the day field, it's day.
    • 0:26:34And these exactly are the parameters that will help us grab these values.
    • 0:26:39And the way that we do it is by using requestform.get and then inside
    • 0:26:45of the parentheses specifying that specific name of the input
    • 0:26:49that you want to grab.
    • 0:26:50For example, in this case, it would be friend.
    • 0:26:53And again, you can see the top part being the app.py and the bottom part
    • 0:26:59being the HTML tag that we had already pre-created.
    • 0:27:05So that is the grabbing of the value.
    • 0:27:07But how do we actually update the model?
    • 0:27:12Well, this is where SQL comes back in.
    • 0:27:15We didn't learn it for nothing.
    • 0:27:16We didn't learn it just to do [INAUDIBLE].
    • 0:27:18This is where we can use SQL and create even more complex websites.
    • 0:27:23So before we jump into that, let's go back and let's see.
    • 0:27:32Let's try to take a look at the database.
    • 0:27:40It's birthdays.db.
    • 0:27:44Open anyway.
    • 0:27:46So this is a visual way to look at the database.
    • 0:27:50So what we already have in here is a table called birthdays.
    • 0:27:56So if we click on it, we can see that there's
    • 0:27:58already some information, particularly from the Harry Potter movies.
    • 0:28:03So Harry, Ron, and Hermione, their birthdays.
    • 0:28:06We can then also look at the structure of this table that
    • 0:28:10was already created for us.
    • 0:28:12So you can notice that there are four columns.
    • 0:28:16The first one is ID, and it's something that, again, computer just
    • 0:28:19takes care of on its own.
    • 0:28:21We don't have to specify it, as it's a primary key.
    • 0:28:24And then the next three columns are name, month, and day,
    • 0:28:30which is exactly what we were just building together in our HTML page.
    • 0:28:35So these are exactly the values that we want to update.
    • 0:28:39And so going back to our slides, the way that we
    • 0:28:42do that is by inserting some data into the table.
    • 0:28:47And while we haven't really practiced that during week 7, the concept of it
    • 0:28:52is very similar to what we've seen before, having the keyword,
    • 0:28:56specifying the table and its columns, and the exact
    • 0:28:59values that we want to input.
    • 0:29:03And again, sort of like the glue that connects us all is the controller.
    • 0:29:07So the app.py application where we would also do db.execute and use our SQL
    • 0:29:15commands instead of just typing them into the terminal.
    • 0:29:18We just combine everything together in our Python file.
    • 0:29:22And actually, it's very simple in a way that beyond just adding db.execute,
    • 0:29:28your queries stay the same.
    • 0:29:30So you would probably type something like select star
    • 0:29:35from birthdays where month equals, for example, 12 in your terminal.
    • 0:29:39Here we're just adding this additional component of dynamism.
    • 0:29:43So I don't want to be always querying for month equals 12 or month equals 11.
    • 0:29:51I want to have this component be interactive and have users input to it.
    • 0:29:56And to do that, we can use this question mark operator that would just
    • 0:30:01be a placeholder for whatever we want, for whatever value we want to give you.
    • 0:30:06And the idea is very similar to the one we had in C, where, for example,
    • 0:30:10we had %i for integers or %s for strings having that placeholder and then
    • 0:30:16specifying the variable which value we want to take after the comma.
    • 0:30:22And as we see, we can have multiple values inside of the query.
    • 0:30:29So in this case, month and day just go comma by comma after the quotes.
    • 0:30:39And while it didn't necessarily show you how to do the insertion,
    • 0:30:44again, the concepts are very, very similar
    • 0:30:47and we will do it together in just a second.
    • 0:30:54OK, so let's go back to our cs50.dev and now implement the update of the model.
    • 0:31:06So something that will help us make things even more interactive.
    • 0:31:11Let's go back to cs50.dev and now let's implement
    • 0:31:14the controller component that will help us make this more interactive.
    • 0:31:18And like I just mentioned on the slides, we're
    • 0:31:22going to use request.form.get to get those components from the inputs.
    • 0:31:27So using request.form.get in this case, I want to grab the friend input.
    • 0:31:35Next, I want to grab the month input.
    • 0:31:43And finally, day.
    • 0:31:46So exact same syntax for all three of them, just getting the user's input.
    • 0:32:04Let me kill this.
    • 0:32:05OK.
    • 0:32:06Just grabbing user's input from the web page.
    • 0:32:11And now let's actually insert it into the database.
    • 0:32:14So, as I just mentioned, it would just be using the db.execute function
    • 0:32:21where db is the database that we actually pre-opened before.
    • 0:32:27So I'm not just putting db and hoping it'll figure it out.
    • 0:32:29You still need to specify it.
    • 0:32:31And definitely feel free to poke around afterwards,
    • 0:32:33like trying to understand all the other lines.
    • 0:32:36But this is where activate the birthdays database that we're then
    • 0:32:40are able to reference to just as db.
    • 0:32:44And then inside of the parentheses, we're
    • 0:32:47just going to write our regular SQL query.
    • 0:32:51So again, having our keywords the table and then specifying one by one
    • 0:33:00the columns into which we want to input these new values.
    • 0:33:07And again, the syntax might be a little weird,
    • 0:33:09because we didn't really work with insert a couple of weeks
    • 0:33:13back, but still the same ideas.
    • 0:33:17And then as I mentioned before after the comma,
    • 0:33:21we are specifying the inputs that we want to give.
    • 0:33:25So notice how all three things sort of correlate with each other.
    • 0:33:30So in the columns, the order is name, month, day.
    • 0:33:34And then for the values, we also want to make sure
    • 0:33:38that they are in the same order as the column
    • 0:33:42so that we are inputting information into the right things.
    • 0:33:46So let's save this.
    • 0:33:49And let me clear the terminal and let me run flask again.
    • 0:33:57Let's see.
    • 0:34:02All right.
    • 0:34:03So now if I type Yuliia and I type month and day and I add birthday,
    • 0:34:11nothing really happens here.
    • 0:34:13But if I go back to cs50.dev and, let's see, maybe open a new terminal.
    • 0:34:24And CD into my folder.
    • 0:34:27Run SQLite birthdays.
    • 0:34:32SQLite 3 birthdays.
    • 0:34:36And here I do select from birthdays just to see everything that's inputted.
    • 0:34:43And now see that in addition to Harry, Ron, and Hermione,
    • 0:34:47there is now my name on the very bottom, because now we've
    • 0:34:51added this new layer of interacting between the view
    • 0:34:55that the user sees, information that they put in, with our database
    • 0:34:59so that we can actually store all of this data inside of it.
    • 0:35:07And again, as with the min and the max that we just implemented in the input,
    • 0:35:12there are always some guardrails you need
    • 0:35:15to think about when you're implementing especially users facing websites.
    • 0:35:21So if we thought about implementing the min and max for day and the month,
    • 0:35:28here we also want to consider has the user even inputted anything?
    • 0:35:33We don't want to try to insert an empty day or an empty month into our database.
    • 0:35:38So one really good check when working with users inputs on the websites
    • 0:35:43is checking if the input even exists.
    • 0:35:46And for that it's slightly abbreviated, but if not name, essentially we'll check
    • 0:35:52is the name empty or not.
    • 0:35:55And if it is, we just want to redirect back to the main page,
    • 0:36:00sort of like re-prompting the user in a way to input the correct--
    • 0:36:11to input any information, in fact.
    • 0:36:14So very similar idea for all of them.
    • 0:36:19So making sure we redirect in case they don't input anything.
    • 0:36:25Let me rerun this.
    • 0:36:29For example, now if I try to input David's birthday,
    • 0:36:32but I accidentally skip a month and I just add a day,
    • 0:36:36again, nothing necessarily happened here.
    • 0:36:38But if I go back to--
    • 0:36:45let's see.
    • 0:36:48I go back to my SQL query and I select from birthdays again.
    • 0:37:03That was not supposed to happen.
    • 0:37:05Let me see what I might have done wrong.
    • 0:37:12It's not supposed to get to this line.
    • 0:37:17Oh, OK, I missed return.
    • 0:37:19So redirect will redirect it to the right route.
    • 0:37:23But you also want to make sure you include the return statement,
    • 0:37:26which I didn't.
    • 0:37:27So let's try this one more time.
    • 0:37:30So maybe if I want to input Carter's birthday and then I miss months,
    • 0:37:35but I do input the day and then I add birthday,
    • 0:37:38let's try to see if that shows up in the database.
    • 0:37:43And it doesn't.
    • 0:37:44OK, yay.
    • 0:37:44So it is working as intended, but I did miss the return statement.
    • 0:37:48So make sure you are adding that as well.
    • 0:37:52And again, there are different other guardrails that you can implement here.
    • 0:37:56So as I mentioned earlier, you can think about cases where if it is February,
    • 0:38:01you want to make sure that the day doesn't exceed 28 or for certain months,
    • 0:38:06it can only be up to 30.
    • 0:38:08So definitely some other components that you can
    • 0:38:11think about when making the websites.
    • 0:38:13But definitely this is the part where we want to make
    • 0:38:15sure we're getting very defensive.
    • 0:38:18We need to think about all the possible errors
    • 0:38:21that the user can make and try to think of ways of steering them
    • 0:38:26into other direction or preventing these errors from happening
    • 0:38:29in the first place.
    • 0:38:33OK, that was a lot.
    • 0:38:35Our first integration of database and the HTML together.
    • 0:38:41So let me take a pause and ask what questions do we have
    • 0:38:49before we jump to our last few parts.
    • 0:38:59Yeah, so very good question.
    • 0:39:01Why are we using input and not button?
    • 0:39:05So it's sort of an idea that we want to make sure that we are submitting it.
    • 0:39:10And let me actually test if it works the same with button.
    • 0:39:18So with buttons actually not showing up properly either.
    • 0:39:22And input is what allows us to actually have this interaction between app.py
    • 0:39:30and HTML.
    • 0:39:31And the button part of it on the HTML that we saw comes from the submit.
    • 0:39:36So it's almost like, yes, it is an input,
    • 0:39:39but we're not explicitly typing anything in there.
    • 0:39:42We're submitting it.
    • 0:39:44But this is what creates this connection between the HTML and app.py.
    • 0:39:54Any other questions we have while we're here?
    • 0:39:57And again, this is so many different layers
    • 0:40:00that we are adding on very quickly.
    • 0:40:04So definitely feel free to take a look at it afterwards
    • 0:40:07and sit down on your own to really think it through it line by line.
    • 0:40:11But for now, let's go back to our last piece
    • 0:40:14where we're actually going to render the templates.
    • 0:40:18So far, we've just been outputting the regular HTML
    • 0:40:22files, something that was very common when we just worked with HTTP server
    • 0:40:27in week eight.
    • 0:40:28But now that we have the component of the database and the app.py,
    • 0:40:32we can even make the generation of these web pages more interactive,
    • 0:40:36not just like the static, universal ways of outputting things.
    • 0:40:41So for example, if we want to tailor the messages, if we had like an index.html
    • 0:40:51and all we wanted to say is the message is x,
    • 0:40:55this is how we might want to do it.
    • 0:40:57And someone asked earlier, but this is where we actually encounter Jinja.
    • 0:41:02And that's what really helps us make these websites interactive and creating
    • 0:41:07sort of a different layer of interaction between the app.py and the HTML files.
    • 0:41:14So by using this function called render template and specifying in the quote,
    • 0:41:20the specific template that you want to render,
    • 0:41:22then you can provide these tailored outputs in different variables.
    • 0:41:28So in this case, we want to specify that the message is hello.
    • 0:41:31And you can certainly add more than one variables
    • 0:41:35to the render template function.
    • 0:41:37In fact, we can also play around with lists.
    • 0:41:40So this is something we're going to see in just a second,
    • 0:41:43and it's very similar to what we are going to do.
    • 0:41:45But you not only can pass one variable.
    • 0:41:50You can pass lists and dictionaries.
    • 0:41:53And on the bottom is how we would operate it.
    • 0:41:57Instead of maybe saying birthday one is February 2 and birthday two is
    • 0:42:01February 28, because that's certainly not how we store information,
    • 0:42:04passing in the entire list and then using Jinja syntax and the HTML file,
    • 0:42:11we can have this dynamism in our web pages.
    • 0:42:17So having again, this Jinja syntax is really helpful for us
    • 0:42:21because we can create this dynamism again, but on a different level
    • 0:42:25where we now have interaction between the app.py feeding information
    • 0:42:29into the HTML file and we can then generate some tailored templates, again,
    • 0:42:34depending on where you are on your web page.
    • 0:42:39And lastly, the dictionaries as well, which
    • 0:42:41is a very powerful data structure we've been using, sort of similar idea,
    • 0:42:45just slightly different syntax, is if we used just for birthday and birthdays,
    • 0:42:53outputting the birthday, which is the syntax very similar to Python
    • 0:42:57in dictionaries, we instead want to access the keys using the dot
    • 0:43:03notation so that we could access the specific values that we're looking for.
    • 0:43:11And what I've been referencing so far is Jinja.
    • 0:43:14So something, again, we saw in the lectures.
    • 0:43:17And the main two differences is that when
    • 0:43:20we are working with for loops or if statements,
    • 0:43:26the Jinja expression is using the curly braces and the percentages.
    • 0:43:31But when we're working with Jinja variables,
    • 0:43:33we want to make sure we're using two curly braces to encompass that variable
    • 0:43:38and then output it on the web page.
    • 0:43:41So for the final time, let's go back to cs50.dev and finish implementing our app
    • 0:43:48so that we have a final project.
    • 0:43:51Actually, let me kill all of these terminals
    • 0:43:54so that I don't confuse myself.
    • 0:43:59All righty.
    • 0:44:04Let's make a new one.
    • 0:44:05Perfect.
    • 0:44:09All right.
    • 0:44:10So let's go back to app.py.
    • 0:44:13And we've so far created the get part of things.
    • 0:44:17So if request method is--
    • 0:44:19sorry, the post side of things.
    • 0:44:21So if request method is post, it means we're submitting a form
    • 0:44:24or the user has submitted the form.
    • 0:44:26And so we're grabbing all of this data and we're putting it into the database.
    • 0:44:30And then we're just sending them back to the main page.
    • 0:44:33But again, let's consider the scenario that we talked about in the beginning.
    • 0:44:36What if the user just navigates to the birthdays page?
    • 0:44:42They haven't submitted anything.
    • 0:44:43They haven't touched any buttons.
    • 0:44:45They just want to see all of the birthdays that
    • 0:44:48have been outputted there so far.
    • 0:44:50And this is where, again, we come back to SQL and create
    • 0:44:54this interaction level.
    • 0:44:55And we might want to query for all of the birthdays that already exist there.
    • 0:45:02And so again, using db.execute, db being the birthdays database,
    • 0:45:07we can simply just grab all of the birthdays,
    • 0:45:11like all of the data, the names, and the month
    • 0:45:13and the day from our birthdays table.
    • 0:45:19And then after that, as we just saw on the slides,
    • 0:45:22this is, again, where we have this two way control.
    • 0:45:26And by rendering the template, we can now
    • 0:45:30also specify what birthday, like what information we want to input.
    • 0:45:34And in this case, it would be birthdays.
    • 0:45:37So again, if we didn't have it, index.html
    • 0:45:41would just be the plain submit form.
    • 0:45:43We wouldn't see anything else.
    • 0:45:45And also if we just specified, hardcoded all of the names and all of the dates
    • 0:45:51and all of the months, what happens if I have a new friend
    • 0:45:54and I want to add a new birthday?
    • 0:45:56Then I would have to go to HTML, manually added it there.
    • 0:46:00It will also be harder to store it.
    • 0:46:02So having this three way interaction of having a model with the database that
    • 0:46:08stores all of the information, the controller that interacts now
    • 0:46:12between the model and the view side, we can now
    • 0:46:15create more, again, dynamic and tailored web pages.
    • 0:46:19And now that I've passed into the template the birthdays information,
    • 0:46:26we now can go back to our HTML file and now actually loop through these entries
    • 0:46:34so that we can actually output something.
    • 0:46:36So let me get on the new line here.
    • 0:46:41And this is where we're going to use Jinja syntax, which
    • 0:46:45might be slightly unusual to what we've seen before,
    • 0:46:48but actually it's very similar to Python.
    • 0:46:50We just have these specific syntax that we need to account for.
    • 0:46:56So as I mentioned before, if we're using a for loop or an if statement,
    • 0:47:02the syntax would be curly braces and percent sign.
    • 0:47:05And then after that, it's very similar simple syntax to what we have in Python.
    • 0:47:17One important thing, though, is any time you start a block, so in this case,
    • 0:47:23it would be a for block, you want to make sure you end it.
    • 0:47:28So since we started it here for birthdays,
    • 0:47:33we want to make sure we end it whenever we don't want to loop through it
    • 0:47:36anymore.
    • 0:47:37And now inside of it, I'm just going to create a row tag.
    • 0:47:42And then inside of that, the actual element tags.
    • 0:47:51I want it like that.
    • 0:47:52OK.
    • 0:47:52And then on the other line, I'm going to create another element tag.
    • 0:48:00Inside of them now, we're going to use slightly different Jinja syntax just
    • 0:48:06being set of two curly braces now.
    • 0:48:10And inside of them, we're going to specify what we want to grab.
    • 0:48:17But this is the roadblock that we run into.
    • 0:48:21What data structure is bday?
    • 0:48:25Birthdays would just be whatever all the SQL queries we ran
    • 0:48:29and the information we got back.
    • 0:48:31But actually now that I'm thinking, what is birthdays data structure either?
    • 0:48:35What do we get back when we run a query through db.execute in Python?
    • 0:48:42What kind of data structure do we get back?
    • 0:48:48Yeah, so it's actually a list of dictionaries.
    • 0:48:52So let's see what the best way to do it.
    • 0:48:57So if I print birthdays here, and let me do
    • 0:49:06flask run so we can open this in the browser.
    • 0:49:15So I actually didn't even need print.
    • 0:49:18So before I actually specified how I want to access it,
    • 0:49:22we can see that this is essentially what we get back from our database.
    • 0:49:28So it's sort of already parsed into one by one element,
    • 0:49:33but essentially it is a list of dictionaries,
    • 0:49:35and each dictionary represents the specific row in our database.
    • 0:49:41So the first row was Harry, was the month and the day,
    • 0:49:45and so forth and so on.
    • 0:49:48And I think if I go back to cs50.dev and I look in here,
    • 0:49:55we can actually see the square brackets in our output.
    • 0:50:01So the square brackets would indicate the list.
    • 0:50:05And then inside of it, we see that it is a dictionary.
    • 0:50:09So by looping birthday and birthdays, I get one element
    • 0:50:15in my list, which is a dictionary.
    • 0:50:17Then I need to go one step further and actually grab
    • 0:50:23the values that I am interested in.
    • 0:50:25So in this case, name.
    • 0:50:28And then on the second line, I want to grab birthday month and birthday day.
    • 0:50:47And again, since it's just text, it's just variable text,
    • 0:50:52it still works like an HTML file.
    • 0:50:54So if I want to specify month slash day, I could use the simple syntax
    • 0:51:01in the same way that if we just had, say, December 3rd,
    • 0:51:08we could just do this.
    • 0:51:09So treating it as simple text, but the one that can just change.
    • 0:51:15So let us save this and let us go back to the birthdays page.
    • 0:51:23Let me zoom out for a second.
    • 0:51:24And now we see all of the birthdays outputted.
    • 0:51:28David doesn't have a date of his birthday.
    • 0:51:31But all the others have the specified names, the birthday month,
    • 0:51:38and the birthday day itself.
    • 0:51:44So maybe let's try playing around a little bit with it.
    • 0:51:47So if I wanted to add, say, Carter and maybe July 14.
    • 0:51:55Now that I added it, notice how it refreshed.
    • 0:52:01And now if I scroll to the very bottom, fingers crossed,
    • 0:52:04I see Carter's name added was the birthday that I just inputted.
    • 0:52:10And then let's play around with a couple of things.
    • 0:52:13So as before, maybe let's test for very weird values.
    • 0:52:23Try to submit this.
    • 0:52:25It doesn't work.
    • 0:52:26And notice how it will just look at the very first one that went wrong.
    • 0:52:31It knows that if the month is not right, I need to focus on that first
    • 0:52:36before I even look into other things.
    • 0:52:38So now if I fix it to March, for example, now it's focusing on day
    • 0:52:43and saying me that that should be less.
    • 0:52:46But if I forget, for example, to input the day for Alice,
    • 0:52:51ideally we shouldn't see anything in the database.
    • 0:52:56And let's scroll down.
    • 0:52:57And we didn't.
    • 0:52:59And that might not be super user friendly,
    • 0:53:02because if they accidentally mistyped it and then they
    • 0:53:05don't see it outputted on the table, that's not very intuitive why.
    • 0:53:10But adding some alerts and maybe some other visuals
    • 0:53:14could be helpful for making it more user friendly.
    • 0:53:17But I think for now, we have a pretty good bare bones
    • 0:53:20website that would allow us to keep track of our friends' birthdays.
    • 0:53:26OK, this was a lot today.
    • 0:53:28So let me take a pause and for a couple of minutes
    • 0:53:31take our last minute questions that we have about flask.
    • 0:53:43So someone asked about the syntax for the dictionary.
    • 0:53:47And that's, again, just how Jinja works.
    • 0:53:50So yeah, in Python, what we did was the dictionary name, square brackets.
    • 0:53:58And inside of the square brackets, we would specify the key.
    • 0:54:02But in Jinja, we just use a dot notation that is slightly different
    • 0:54:06but still is serving the same purpose.
    • 0:54:10And for adding alerts, you could maybe even--
    • 0:54:13it's something you'll see in finance.
    • 0:54:15So to tell the user that maybe something went wrong.
    • 0:54:19You can have a separate HTML page where you
    • 0:54:22would render a tailored apology or a message to the user.
    • 0:54:25For example, if we are error checking and there is no month inputted,
    • 0:54:34we could, instead of just redirecting back to the main page,
    • 0:54:39we could render a template specifying a message to the user,
    • 0:54:44making more user friendly and intuitive, saying please input the month
    • 0:54:49or you forgot to input the month.
    • 0:54:52This was CS50 Week 9, and this would be our last section of the semester.
    • 0:54:56So thank you so much for tuning in, and see you next year.
    • 0:54:59Bye.
  • CS50.ai
Shortcuts
Before using a shortcut, click at least once on the video itself (to give it "focus") after closing this window.
Play/Pause spacebar or k
Rewind 10 seconds left arrow or j
Fast forward 10 seconds right arrow or l
Previous frame (while paused) ,
Next frame (while paused) .
Decrease playback rate <
Increase playback rate >
Toggle captions on/off c
Toggle mute m
Toggle full screen f or double-click video