CARTER ZENKE: OK, well hello, one and all, and welcome to CS50s week
9 section, this week where a lot of it comes together.
My name is Carter Zenke.
I'm the courses preceptor here on campus.
And the goal of these sections that we host online
is to help you bridge the gap between lecture
and this week's problem you'll be working on.
In this case, you're building our own web application using Flask.
And so today we'll dive into these three topics here.
Flask and Jinja, how we can use forms in our websites,
and how we can work with databases and build more dynamic applications
overall.
So I thought to kick things off, we do what we normally
do and maybe on the count of three, we'll all unmute and say hello,
so a big chorus of hellos to kick things off.
So on the count of three.
We'll do one, two, three.
AUDIENCE: Hello!
CARTER ZENKE: All right.
It's so good to hear all of your voices here together.
And again, our goal will be to go through these three topics right here.
And again, this is going to be interactive.
So if you have questions about these three topics as we work through them,
please feel free to message them in the chat or chime
in when I ask for responses here.
Now we'll dive into Flask and Jinja.
And as we saw in lecture, Flask and Jinja are these two--
well, Flask is more like a framework we can use
and Jinja is this templating library we can use along with Jinja.
But the goal is to build more dynamic applications than you could
with just HTML, CSS, and JavaScript.
So we saw those three languages--
HTML, CSS, and JavaScript-- in the prior week, week eight.
But we saw some limitations of them.
Maybe if you've worked on the homepage problem--
you've been doing a lot of copying and pasting,
or you weren't able to do everything you wanted to do.
Well, Flask comes in to help you build something that
is going to be more dynamic for you.
And one of my favorite examples of this, of a more dynamic website
that Flask can enable us to build is something like this one right
here, libraryofbabel.info.
And you can actually go to this site.
This site is based on the book, Library of Babel by Jorge Luis Borges.
And in this book, this author imagines this library
that has all the possible stories that have ever
been written, the stories have been written before us,
ahead of us, currently right now, because every book contains
these random assortments of the 26 characters.
This is an infinite library with infinite books
and contains all the possible stories that have been written
or could be written.
And we can actually build our own website along the same,
but let's check out this one just to be sure.
So you can go to this you URL right here, libraryofbabel.info.
And I'll do the same over here on my computer.
I'll go over here.
And you might see a home page like this, if I zoom in for you.
And you can see we can browse this library to see what is inside of it.
I'll click browse here.
And maybe I'll choose to go to like the first section of this library.
I'll go to section zero down here.
I'll click on it.
And I see I'm in this room with four walls around me.
So maybe I'll click on this wall, wall one, and I'll see this shelf of books,
right?
And I'll look, maybe just at this third shelf, just randomly, now.
And I'll go down to, let's say this book right here.
Let's pick one randomly from the shelf.
And you'll see that I have this page full of just
lots of random bits of text.
But the idea is, there are so many books in this library,
full of so many pieces of random text, that all the stories that could
possibly be made are somewhere in here.
And so if you take my word for it, we actually
go to the search function for this website.
I'll go back to the home page here by going to that slash route.
And I'll go down to search at this point.
Maybe I'll search in this library.
And I can Enter up to a certain number of characters here.
Maybe I'll search for this is CS--
and I can't type 50.
I'll say this is computer science fifty week nine section.
I'll zoom in so you can see it there.
And I want to find out where is this text in this infinite library of books?
So I'll go down and I'll hit Search.
And I can see down below, it shows up in a few different places.
I can see the places it shows up with random characters around it.
So I'll click this one, it looks like it's on page 61.
I can see it highlighted here.
This is computer science fifty week nine section--
whoops-- right down in here.
And this is on page 61 of some random book
that we can see the location of down here.
And we can go back over here.
We can search for other places, too.
Maybe with some other English words.
This is another book that it's in right over here.
And so we can look at all of these books and see all the places
that this phrase shows up.
And all this is done just by virtue of taking in some random text
and figuring out what it might make out of the randomness that we have here.
So if we think about how this website is made, let's think about some
of the routes we can go to-- some of places
we can explore at the very basics first here.
So if we go back to our slides over here.
We think of a website often having these routes
we can go to see different parts of it.
And so if I look at this, you'll notice that I have this slash at the end.
If I go to libraryofbabel.info and put a slash at the end,
I'm asking for the home page of this site.
Give me the very basic page of this site.
The very initial site I want to see.
But I could also go to, as we saw, slash-- this is the home page here.
I could also go to slash browse.cgi.
And this is just a name that the author of this website
kind of made up browser-- browse.cgi sounded OK for their purposes.
And when I ask for this route, browse.cgi,
we'll all see something different.
I'll go down here and see I--
OK, I can see some library of books I can click on and browse in this case.
And because I have all of this, I'm able to see different parts of the website.
And I ask the server different pieces to give me back different other pieces
of the site I'm looking for.
We could even, if we wanted to, add some parameters to it
and look at particular books, if we wanted to, using something like this,
having parameters and these dot dot dots at the end here.
Now how is this really working?
You can imagine this website is full of these HTML pages look a bit
like this, right?
And maybe if you were working on home page last week,
you could think of it like this.
Or we have maybe trillions of HTML pages full of different text.
And let's see.
So if I were to request a page from this server right here,
I can make an HTTP request.
I might say this.
Give me the slash browse.cgi page.
I'll do this.
You're going to get back a 200 code for everything's OK,
and give me back that page over here.
But now, if I wanted to see the text of this site,
well, I could think of it a bit like this.
Go over here.
I might see on the right hand side, well, some text up here on--
sorry, the left hand side and then some HTML on the right hand side.
And again, if you're working on a homepage last week,
you might have seen something like this, where
I could actually try to put in the random string of text here.
But how many pages would we need to have all of this kind of text?
If it's all random, it's all a combinations of these 26 characters,
how many HTML pages would we possibly need here if we had so many of these?
Feel free to chime in in the chat.
How many would we really need to have all combinations of these 26
characters?
I'm seeing a lot.
We need a lot of pages, right, for all of these 26
characters, all combinations of them.
And so if we had trillions of these pages,
well, that's not very efficient for us.
We could do, instead, probably better, is we could actually have a single HTML
page and we could just kind of slot in some random string of text to that page
using something like Python or Flask, in this case.
So we could do something like this, where
instead of having millions and millions of pages that
have all these random strings of text, we could have one page
and we could use Python to slot in a single random string for us--
perhaps in this placeholder right over here.
So let's actually explore how we could build
our own mini-version of this website, where
we can generate random strings of text and see
if we find any interesting, useful information from that piece of text.
So I'll go to my code space over here.
And I've already set up this Flask application.
I can zoom in a bit.
And I'll type ls.
And you can see I have these files down below.
I have app.py, I have helpers.py, which is a similar file name
you'll see in this week's problem set.
I have history.db, and I have a templates folder.
And just to refresh our memory, what do you think will go in app.py?
I mean, what language is that written in and what do you
think we'll put inside of it, as we saw in lecture?
Feel free to chime in in the chat.
What will go inside of app.py.
So I'm seeing Python, Flask.
Right, yeah, this is we're going to write some of our code
that will be written in Python, and we'll
use the Flask library inside of app.py.
So I can open that up a little bit.
I'll code app.py, and we'll get you a taste
of what this is doing for us, which we'll explore in much more depth
later on.
OK, this is our basic Flask app.
Now though, I also have history.db up here.
And knowing what we know about dot db files, what kind of file is this?
History.db.
It's a database, right?
So we have some application, app.py will write our Flask code,
and we also have this database called history.db.
And inside of templates, what do you expect we might find?
If I were to cd into templates to look inside.
HTML, right.
So I could cd templates here.
I'll type ls, list my files.
And I'll see two HTML files, index.html and layout.html.
So let's get a feel for what this is doing for us already.
So I'll type ls to see my application files here.
I'll then type Flask run to run this application.
This is different from HTTP server.
I want to run this application using Flask.
So I'll do Flask run, hit Enter.
And now I should see shortly a URL pop up, or I can open it down below.
And now I should see this very basic website, Library of Babel
with a search history down below.
Now this is not presently done yet.
For one, there's no text on the screen.
Ideally, you'd want to have some random string of tax we'd actually
generate every time we visit this site to see
if we get any meaningful words from it.
So if we look at our application here, I'll
go ahead and open up a new terminal.
And I will get back to my library folder and I'll
code app.py to pull up this file.
So here is our very basic Flask application,
if y'all can see at this text size.
Let me know if you can't.
From lines one through four, what would you say we're doing here?
If we're going to break this down top to bottom, what are we doing in lines one
through four?
What's the purpose of these lines of code, just in general?
Seeing some imports.
Other things you might think, too?
Setting up library access, yes, definitely.
So here on lines one through four, we're simply telling our application,
these are the libraries we're going to use in our program.
Pieces of code somebody else has written for us.
We'll use these in our application.
Notice how I'm using the CS50 library here to get something called SQL.
I'm using the Flask library here to get something called Flask, also,
and also this render template function, and something called request.
And I also have from helpers import random strings.
So notice how I had back in my list of files here, helpers.py.
Well, I could open up helpers.py here to show you what's inside.
And I do have this function that I defined called random string.
And if I give it some variable called len, some argument called len,
I'll then get back a random string that has that length of characters.
So if I said random string 500, I would get a string of 500 random characters.
And I made this myself.
But instead of putting it inside of my app.py, what I did instead,
sort of copy and paste this into app.py, is I said, well,
I want to keep things separate here.
I'll put this helper function inside of helpers.py.
Similar to what you'll see this week with the finance problem set over here.
OK.
And then finally, we're just importing the random library
so we can get access to some of its functions.
We'll see those later on in a bit.
Now down below is where we get a little more interesting.
So here on line six as we saw in lecture,
we're kind of setting up our Flask application to run.
You could think of this piece of code, right here,
capital F, Flask, as being this template for a Flask application,
that comes with some built in functionality,
like the ability to say what we want to do when we visit a route, for example,
on our website.
But here we're saying, let's make a particular Flask application,
and let's make sure that it corresponds to this current file--
that's what dunder name here it's something
for us-- underscore underscore name, underscore underscore [INAUDIBLE]
dunder name, is going to say build me a particular Flask app based on this file
right here.
And for the rest of this file, let's just call it app.
We could call it something else.
We could call it library here.
But notice how we have to change everything else.
We have to say library.config or library.route.
So in the end, it's probably easier to call it app throughout,
this kind of short name we can use to refer to our own Flask application
we're building from this file.
Now down below, we also have some connection to our database
as we saw in lecture.
This is saying, give me some variable called db,
that I can then use to refer to this database called history.db
that will access using sqlite.
And then down below, let me just have some configuration
to say that I want my HTML to auto reload when I change it.
So if I go to an HTML, make a change, I want
to see that change kind of automatically in my browser.
And then finally, this little bit of code down here--
at app.route.
Where have we seen this slash before?
What does it often correspond to in our applications?
We saw this just a little bit ago.
Yeah, so I'm seeing the root folder, the home page.
Whenever we ask for the slash root of an application,
we're often asking for that main page-- that home page of the site.
And so the hint here is that at app.route, if I say slash here,
that's going to say, well, I'm going to define
some function I can run whenever the user requests
the slash root of my application.
And then down below, notice how I have this function called index.
I'm going to define, again, some function I can use to--
function I can use when the user asks for that slash root,
I will run this function top to bottom every time they ask for it.
And in this case, it looks like I'm just returning
the result of this function, render template index.html.
So I'm giving back the home page, which is what we saw, again,
when I went to my slash root here.
I then see my home page of my site that's inside of index.html.
So questions on this syntax so far?
Feel free to ask in the chat.
This is kind of a lot all at once, but I want
to make sure we get your questions answered.
OK.
So feel free to keep putting questions in the chat.
I see one coming in.
Where are the imported libraries actually imported from?
So presumably, these libraries are somewhere on your computer.
And in fact, they're probably in some kind of special folder for Python.
They might be even brought in from the web a little bit.
I'm actually not quite sure exactly where they are,
but they are somewhere, right?
And Python is getting them from someplace.
So I can't answer your question in particular,
but you could probably look it up online and figure out where are you
importing these things from.
I can tell you, though, that for example,
from helpers import random string, well, that
is being imported right from this file called helpers.py,
that we see is right next to our app.py, right here.
And a question-- what is dunder name doing, dunder name referring
to underscore underscore name underscore underscore.
That is simply a special Python variable name that
refers to the name of this file app.
And so this is a way of telling us that, hey, given this template for our Flask
app that we have, capital F, Flask here, let's go ahead
and build a particular one that's called app, and we'll do it from this file
that we're going to call--
well that is presently called app.
So this gives our application access to all the other kind of files and folders
that matter for our application.
All right.
These are good questions.
So let's keep going on and figuring out how this corresponds
with our HTML for our program so far.
So I'll go back to my terminal.
And all this time code up index.html.
And why don't I put it--
just for the sake of visualizing it-- kind of side by side here.
Do this.
Whoops.
Let me move this over here.
There we go.
And now we can see our Python code, right alongside our HTML code.
And this is helpful because often when we're working with Flask,
you do want to have some kind of correspondence between our HTML
and our Python code-- in some cases, variable names will matter.
And if we're talking about trying to insert some text to our HTML,
well, it's important to visualize those things side by side.
So we'll do that from here on out, assuming you all
can still read the text size here.
So if we visit our page and I refresh it,
I don't really see any piece of text.
But again, our goal was to insert this piece of text
so we could actually see it come to life and we could regenerate some new text
every time we visited the page.
Well, if I wanted to generate just a random string of characters,
I could do that using my random string function up here.
So let me try to use that.
I'll go to index function down here, which, again, is called
whenever I visit that slash root.
I'll put this to the side for now.
I'll come down here and I'll say, I want to get
a string that is going to get me the result of, in this case, random string.
And I'll ask for a string of maybe 1,000 characters, right?
So now if I were to just print string, I should hopefully
see that I have this random string of text inside my terminal,
because print here will print to my terminal.
So open up my terminal.
I'll go back to my server.
And let me go ahead and just refresh the page over here.
I'll hit slash, go to my slash root, hit Enter.
And now nothing changes on my HTML.
But if I look inside of my terminal, I do
see this random piece of text I generated
right, about 1,000 random characters.
So how could I then get this string, perhaps, inside of my HTML?
Any ideas from what we saw from lecture recently?
We have this variable called string.
But how should we get it inside of our HTML?
So I'm seeing using some equals, some maybe braces.
And so you're all on the right track.
So notice how here in our index HTML, we do
have this placeholder already set up for us.
Let me zoom in on it on the right hand side.
You can see we have this placeholder here.
And using what's now Jinja syntax, we can say,
I want to have a placeholder, some value I want to write in to my HTML.
And I can denote it with these curly braces on either side--
double curly braces.
And then the inside of these braces, I can
say what the name I want to-- the name of the placeholder I want to call this.
Here I'm just calling it placeholder, I could also call it string.
I can call it really anything, but here I'll just call it placeholder.
Now if I wanted to pass in string to be kind of interpolated
or put inside as HTML, I could do this.
I could say, when I call this function, render template,
let's go ahead and make sure that placeholder gets the value of string,
in this case, as somebody in the chat has already noted.
Placeholder equals string.
So what I'm doing is taking my Python variable, string, and saying,
let's put it inside of my HTML in the placeholder that's literally called
placeholder.
And I know that it is right here because I
can see it in double curly braces using Jinja syntax on the right hand side.
So now if I go back and refresh the page, hit Enter here,
I do actually see my random string of text.
And I can actually do this again and again.
I'll hit refresh to see a new random text.
Refresh again and refresh again.
And now I see this piece of text, over, and over,
and over again, constantly regenerating every time I call the slash root,
and thus, the index function here.
So what questions do you have on this so far,
and how we're interpolating or adding in some of our variables
from Python to our HTML?
Yeah, so one piece we haven't seen yet--
I see a question in the chat-- is about this methods equals GET.
So here, when we define a route of our application using Flask, we're saying,
here's what the user can type in, in the URL,
to run this function down below, and also,
here are the methods they can use to access this route.
So remember how there are two HTTP methods--
GET and POST-- where GET simply involves the user asking
the server for some web page.
If I say get me this page, it's going to give you back the page I've asked for.
POST on the other hand, is about sending some data through a form, most likely,
to your server.
And the server then does something with that data.
So here, because we're just asking--
because the user is just asking our application for, really,
a random string, we can safely use GET.
But as we'll see later on in just a moment here,
we'll try to make our own form and you can
POST data or send data to our server.
So we should use POST in that case, too.
And I see a question, what is SQLite doing up here?
So notice how, if you recall from week seven,
SQLite is this database engine that we use.
And so this line up here, line eight, db equals SQL,
SQLite colon slash slash history.db is saying,
let's build a connection to our database called history.db using SQLite.
And within Python, let's go ahead and call that connection just db,
in general.
And we can use something like db to execute to run queries on our database
that we'll see later towards the end of section here.
All right.
Other questions, too, on this so far?
All right.
So let's keep going here.
And maybe one thing we'd like to do is actually
have more than one variable inside of our HTML.
So let's say I went up here and I went down to--
I don't want to call it Library of Babel.
Maybe I just want to just call it Library of some name I can pass in.
So to update this, what can I do to the end of render template?
I have this placeholder called name, and it's a placeholder
because I have braces brace, brace, brace, with name on the inside.
So how could I then update this here?
Anyone chime in the chat?
How could I substitute a name in here?
Yeah.
So name equals something.
I can say colon--
or not colon.
A comma, name equals perhaps Carter.
Hope you can see that.
And then I'll go ahead and go over here refresh the page.
And now I see library of Carter.
So here's one more example of having multiple different values
you can pass in to our HTML.
I could change this also to Joel, hit Enter here.
And now-- whoops, let me refresh this.
And now we see a Library of Joel.
So another way of passing in some data here.
And now often what you will see is this variable called placeholder.
Or really, this placeholder called placeholder isn't really
well named, because if you had to call everything placeholder,
it would get really messy really quickly.
So we could just call this also string, in our HTML.
But then what we'd have to do is go down below here
and say, string equals string, which is a little confusing if you're
a beginner, because on the one hand, you're seeing,
OK, string equals string, we know that.
But what this is really saying is that string on the left hand side
is referring to this placeholder string in our HTML,
and string on the right hand side is referring
to this actual Python variable called string.
So often you'll see these two names being the same thing,
but they refer to different objects or different placeholders or variables
in our programs.
So let's go back and call this maybe not placeholder.
Let's call it maybe a random string over here.
And I'll do the same over here, random string,
just so we can keep a good naming system,
but also try to differentiate the Python variable from the HTML placeholder.
OK.
So a question that comes up is, how do we actually get input from the user
now?
So this is all fine and good.
I can go to my URL, I can refresh it.
And I can see-- oops, let me actually change this.
Library of Babel.
Refresh the page.
And I do see random text over, and over, and over again.
But what if I wanted the user to pick out a page,
like they can do in the real version of this website?
They can go to page 10 or page 410.
They can see the same thing every time.
Well, to get input from the user, we'll often want to have a form in HTML.
So a form is going to look a bit like this,
where a form is comprised of this open form tag and this closing form tag.
And any ideas as to what often goes inside of a form?
This form is blank.
We don't see anything.
But what goes inside of a form?
Could be a table, some input, other things, too.
A submit.
What else might go inside of a form?
A button, a label.
Yeah, there are lots of things you can add to a form.
And to add them to our forms, we usually put them inside
of these opening and closing tags here.
So form, the opening tag, and slash form the closing tag.
So what I'll do here is I will add this input of type text
and this button of type submit.
Now the default type for input is text, and I
believe the default type for a button is submit.
But here, we're just spelling them out deliberately, here.
Where if I were to render this now-- render this HTML on the left hand side,
I might first see this input box, and that shows up
as this input box on the right hand side.
Some place I can type some text.
And then, I would also have this Submit button over here.
It looks a bit like this.
So together, these pieces are part of my form.
And when I click this Submit button, what
will happen is my data will go somewhere--
wherever I tell it to, so long as I specified the right things.
And now what actually is missing from this form?
We have our input, we have button to submit it.
But what are we missing, perhaps, in the top form tag?
I'm seeing an action.
An action and a method, where if we have some data, perhaps in our input,
and we click Submit, well, there's really no place
for that data to go yet.
So we should really specify where that data is going.
So I might do this.
I might say also, make sure this form has particular action and a method.
So to get our minds on what this action is,
notice how we see the slash here again.
So this action is telling us where--
which route we want to actually send our data to.
Is it the slash route, the slash browse route, or a different route altogether?
And then the second one, the request method
is saying, well, how do I want to make that request?
Should I do it via Post or via GET?
And now what might be one difference between using Post and using
GET with a form?
Maybe chime in the chat.
What might be one difference if we used Post versus GET to submit
our data via the form?
So I'm seeing something like--
Post sends data and GET gets it, which is a true kind of by convention.
We can still send data through get, though.
One is shown in the URL and the other is hidden,
which is a more apt description here, where
we're saying, if I send data via Post, that won't show up in the URL.
But if I send it via get it often-- it will show up
in the URL using these URL parameters.
And so Post is good for when we're kind of sending data to the server,
but we don't want people to see.
Or even when we don't really care if people see it,
Post might still be a good method to use.
So let's try building up our own form here.
Let's go to our HTML and see how we have a to do here.
That to do is to implement a search form.
So if I wanted to make this form, what could I do?
I can go ahead and do form.
And then I need to have some kind of text input for the page number
the user is inputting.
Let me actually make this over here and make it a little bigger.
What kind of input element should I make here?
Any ideas from the chat?
What kind of input element do I need to get a page number?
I could use a label.
Yeah, so I'm seeing something a bit like this.
Input and the type should be, in this case, let's just stick to type text,
assuming the user can type in some text.
We could also, if we wanted to, make this a number.
But let's stick to text for now.
And down below, we might also have a button to submit this.
I'll say button, and the type of this button is a submit button.
And then on the inside I can say whatever I want the button to say.
I could say submit or I could say Go to Page.
And now if I refresh my page, I should see my very own form up above.
And let's just check.
I Can change this to maybe number, here.
Refresh.
And now I can actually have these up and down arrows for the page number.
Here, though, we'll actually just stick to text, just
to keep things a little more simple here.
And now what am I still missing from this form?
We have our input and our button.
What have I maybe missed out on?
The action.
So I'll say action equals slash, because I want
to send this data to the slash root.
And the method in this case will actually be Post.
I want to use a different method.
And notice how in HTML, actually these methods are lowercase
instead of Get capital or Post capital, we use post lowercase or get lowercase.
OK.
So now with this form, I can go over here, refresh the page.
And I'll go to maybe page 10.
I'll hit 10 here.
I'll say, go to page.
But I get method not allowed.
So what have I also not done yet?
I've made my form, it submits via post.
But what have I not done yet in my Python?
Yeah.
So I need to add this POST method here.
I need to say that in the list of supported methods, POST is one of them.
And now if I want to do something different when the user
requests via POST, I could do this.
I could say, if request.method is POST or equals POST,
do whatever is inside of here.
I'll leave it unimplemented for now, but there's presumably something
that we might do inside of this.
So if I refresh the page now, I'll hit Enter, and I can do 10 again,
and 10 again.
And it seems to work for me.
But what we haven't really yet done is actually gotten the data
from the user in our Python code.
So presumably, the user is submitting via POST.
And we could confirm this by saying submitted via POST, printing that out.
I'll open up my terminal.
I'll go over here, submit my form.
And I do see submitted via POST, so that seems to work.
But what we haven't done has actually gotten the data from the form itself.
So how could we do that?
If we go back to our slides here, we might
see that we could actually use this correspondence between our HTML
and our Python code to get the values we want to get.
So notice how in addition to a type, which is now omitted up here
by default. But now we have the name attribute of our input.
And this one is called page.
We could reference that very same name in our Python code
to then get the value that that input holds
at the time the form is submitted.
So I could use request.form.get, and then say page to get access
to the value of that name--
or that input called name in my HTML.
If I submitted via GET instead of POST, I could use request.args.get.
So again, POST is request.form.get, get is request.args.get.
So let's try this out.
Let's go ahead and go back to our HTML.
And let's actually add a name to our input.
This one I'll call--
this one I'll call page.
And now how should I get that value inside of my Python code now?
I have this input named page, but how should I get it in my Python code?
I can use something like the GET function.
But more specifically, what kind of line should I write?
So because I'm submitting via POST, what I should really
do to get access to this data is I could do request.form.get.
So from this request, from the form that was submitted,
get me the value of the input named page.
And why don't I store this inside some variable called page?
Let me scroll this to the right.
And for good measure, I'll print page to be sure I have access to it.
Now I'll go to my code here, I'll refresh the page, hit 10, go to page.
Now I'll look in my terminal, and I do actually
see I have that page number being printed out
because I told Flask to do so on line 17.
I could even make it maybe page 50-- go to page.
And now I see page 50 down below here.
So I've gotten whatever text is inside of my input named page.
And this seems to work for now.
But what if we wanted to update this in some way to actually have some effect?
Well, it doesn't quite matter for the problems
that this week or anything else really beyond this particular application.
But if I wanted to have every page number be set by whatever--
if I wanted every random string be set by the page number,
I could call this function called random.seed.
And I've gotten the seed function, again, from the random library here.
And if I say random.seed page, well, later
on when I generate my random string, if I ask for page 10 and page 10 again,
I'll get that same random string every time,
giving me the illusion of having different page
numbers in my application.
So for example, I can go to my application over here, hit Refresh.
I could say page 10.
And I do see some random string of text over here.
I'll go to page 10 again and I get that same random string.
I go to page 11 and I get some new random string.
Go to page 11 again, and I get that same random string.
So random seed is a way of specifying how the randomness should work.
As long as I pass in that same number--
10, 11, 20 or so on, I then get that same random string
later on when I use the random functions here.
If I don't use POST, though, if I don't run this piece of code,
well, what I'll do is just get a truly random string
based on some random seed.
But beforehand, I could set some seed to account to based on the page to say,
that if I ask for certain page number, I'll always
get back the same string in the end.
OK.
But what might go wrong with this?
If you look at this form, what are some bad inputs you could give to this form.
If we're talking about page numbers, what
are some bad inputs you could give?
Negative nine, letters.
So if I do this, I say, negative nine.
Well, it still seems to work.
But I don't really want it to.
Or if I typed a, a, a, well, that also worked, but not quite the way
I want it to.
And so how could we do this?
I could actually go back to my code space here
and I could try to update this in some way.
So ideally, I want to validate the user input as I get it.
And maybe I have page here.
But I could ask the question, before I do anything with page,
is page less than zero?
And if it is, why don't we return the render template.
Return index HTML now rendered, but at this point just say some error message
like, type in a positive number.
So now my placeholder called random string will actually just
be this error message, type in a positive number.
So now I'll go back over here and I'll do refresh the page.
And I'll type, let's say, negative 10, go to page.
Oh, but I got an internal server error.
And if I look at my terminal, I should see type error less than not supported
between instances of str and int down below.
So what went wrong do you think?
If we look at this code on the left hand side.
What went wrong?
Yeah, we forgot to put page inside of a-- we
forget to convert page to be an integer.
So I forgot to do this.
I forgot to say that if we can, let's make sure we turn page
from a string to an integer.
So whenever you use request.form.get or request.args.get,
whether you're working with numbers or not,
you'll always get your input back as a string, and it's up to you
to convert it to an integer if you want it to be an integer.
So here I'll convert page to an integer.
And now I'll try running this.
I'll go back here and I'll say, slash, to get back to my main page,
and I'll say negative 10.
I'll go to page.
And now I do see, type in a positive number, which is really good.
I'll do -500, and I see the same thing.
But what is the other kind of input we could give to this form that
might make it go wrong?
We've seen negative numbers.
What else?
We can give some text.
We'd say, ABC, go to page, and we get another internal server error.
So something's gone wrong again.
And if we look down in our terminal, we see a value error--
invalid literal for int with base 10 ABC.
And this basically means that I can't convert
the string, ABC, to be an integer.
What does it mean for ABC to be integer?
We don't really know.
So what I should probably do here is use Python's try and accept syntax
where in Python, often it's good to simply try something
that you want to do.
Like try to, in this case, convert page to an integer.
And except, maybe except if I do see that value error,
let me go ahead and actually tell the user another error.
And this time I'll tell them, just enter a number in general.
So again, we'll get the page number from our form.
We'll then try to convert page into an integer.
And if we get that error, we can't convert some text to an integer, what
we'll do is we'll say, I want to tell the user to enter a number over here.
And then finally, as one final check, we'll also ask the question,
is page less than zero?
And if it is, we'll tell them to type in a positive number overall.
So this kind of accesses all the possible combinations of errors here.
So go back up to our website.
Go to slash.
I'll type, in this case, ABC.
Go to page.
And I see enter a number, which is good.
And then I'll type maybe negative 10.
I'll see enter a positive number.
And now if I type page 10, well, I seem to get
the thing I wanted in the first place.
So questions on this and how we implemented some of this
getting of data from the form, but also validating user input,
as you might want to do during this week's problem set?
OK, seeing-- I'll wait just one minute or so.
All right.
What happens, though, actually--
maybe there's one more thing we should take care of--
if I do this.
I could go back over here, put in nothing, and type go to page.
I see enter number, it actually works OK.
But we could also--
well, no, I think we're OK here.
Let me know if you find other errors, though.
Symbols, I saw.
OK, let's try symbols.
I'll type at exclamation point.
Seems to work.
So that seems OK here.
And particularly for this week's problems set.
We will be getting numbers and text from the user,
it's often good to really try to validate the input
to make sure that they're not being adversarial to you.
They're not trying to give you some text and mess up your program.
And you're instead making sure that before you do anything
with their input, you're making sure it is exactly as you expect.
And if it's not, maybe giving them some kind of error as we did here.
So let's keep going now.
And one of our final to-do's was to not just
have the user send some input to our database,
but also to have some search history down below.
And ideally, if I search for page 10, well, I
should see that search show up at the bottom
of my page in this table down below.
And that's not there yet.
But to do this, we'll probably need to use some of our SQL
that we learned from a prior week.
And so let's take a look at how we could use that.
So if we think about databases in our program, they're going to store data,
long-term for our application.
And if we want it to store like a search history,
it's a great use case for that, because we can actually persistently
store search history over time.
So let's take a look at SQL and how we could do this.
If we look at history.db, at the beginning of this session,
I ran this function--
I ran this command called Create table history
with this ID column and this page column, to give me this table of pages
that I could insert into.
And just to show you, if I go back to my code space, go over here.
I could go to my new terminal, type ls, type SQLite3,
history.db to open this up.
And now if I full screen it and zoom in, if I type dot schema,
I see I've ran that command and I have this table called history.
I could even select everything from history, and I see nothing's there.
So I have this kind of empty table in my database called history.db.
So if I were to insert into this table, I could do the following.
I can run this SQL command called INSERT INTO.
And the way this works is we type the name
of the table, so history, and the columns you want to insert into,
so page in this case.
So I'll insert page 50 into my table.
Then I might say, OK, let's insert 100 into my table,
and let's insert 43 into my table as I go through and make these queries.
But right now, this is all happening in the SQLite terminal
and not in my Python program.
So how could I run Python or run SQL but in my Python code?
Any ideas in the chat?
What have we seen so far?
How can I run these same SQL commands but not in the SQLite terminal,
actually in my Python program now?
Yeah.
I'm seeing db.execute.
So I can type the very same command, but just inside of Python
using this db.execute command.
So db to execute is a way of trying to execute SQL commands inside
of our application that connects to this database
and actually runs them on that database.
So here we can say insert in history the page 43.
And one kind of handy thing here is we don't even
need to know the page number originally.
We could just say, insert some placeholder like this.
I could say, make a question mark for some value I could insert later on
and then say comma, the name of the variable I want to insert,
in this case.
So let's actually try this out.
I'll go back to my code.
And let me pull up--
pull up my-- actually, exit from this.
No, I'll keep that up.
Let me go back over here.
I'll code-- oh, cd into library, and I'll code app.py.
And now I see I'm kind of back in my view over here.
I want to insert the page number every time I go to the slash root
and submit the form.
So ideally, when I'm in this post route, I want to keep track of the pages
that the user has requested.
So once I know that I have a valid page number right down in here--
let me zoom in a bit--
what could I run to insert this page number into my database?
I know I can do insert into history into the pages column, some values.
But what do I want to actually type here to run it inside of Python?
This is not valid Python code yet.
But how could I make it so?
Yeah, I can use db to execute.
So I can say, db to execute and wrap this command with that function.
And then to fill in this placeholder here, this question mark,
I could go ahead and say, I want to put in page for that right here.
So now if I look at this entire command, I'm
saying, execute on my database, which I defined up here--
history.db, using SQLite.
Execute on that database this command.
Insert into history, in the pages column, this value here.
And what is the value?
What's the value for page?
And we know, page is a page number the user has submitted.
So let me go back to my application, refresh the page.
This time, I will type page 10.
I'll go to page.
And if I scroll down, well, I don't see anything.
And that's because my HTML--
I haven't actually rendered this.
Like I just see search history but really nothing else over here.
But if I go to my SQLite connection, My SQLite terminal here
and I type select star from history, what
do you think I'll see in this case?
Feel free to type in the chat.
What do you think I'll see when I type select star from history now?
I'll see everything in the database.
And because I submitted this form with the page number 10,
I should see 10 somewhere.
And I do see it.
I see this has an ID and the page is 10.
And now I can go back and submit the form again.
I could go back over here.
Let me search for page 50 now.
I'll go to that page and then I'll go over here.
I'll select everything from history and now I
see both page 10 and page 50 in my history.
So I'm adding to my table, not by being in this terminal down here,
but through my own Python code that I wrote up above, right in here.
OK.
So questions on this so far?
How we're inserting into our table?
I see a question-- how do we see the search history in our HTML
which we'll get to in just a second.
But questions on inserting or this db to execute for now?
All right.
So let's keep going then.
And our last step is really to try to show this search history in our HTML.
And we'll need a bit more advanced syntax, at least
with Jinja on the right hand side here.
But for now, let's figure out, how could I get access to the data
from my database inside of Python, and then
use Python to pass that data into my HTML?
So we're going a few different places here.
We're asking our database, what rows do you have, putting those in Python,
and then telling Python to send that data to our HTML page.
So maybe down here when I visit this page,
let's say I've finished making a request, either via POST or GET.
And now I've generated a string.
But I also want to figure out, what is inside of my database?
Well, I could do db.execute.
And as we saw before, how could I get everything from my history table?
What kind of SQL command could I use?
Yeah, star.
Select star from history, right?
And let me actually add the semi-colon up here, just to be sure.
And now that I've done this, well, I selected everything,
but I also need a place to store this data so I can use it in Python.
And it happens to be that db to execute returns to you
a list of all the rows as dictionaries.
So for example, I could say rows equals db.execute.
And I'll get back a list where every row is a dictionary that has the column
names as its attributes or its keys.
So just to show you--
I could print rows down here, just to my terminal.
And now I'll go to Library of Babel.
I'll hit slash now.
I still don't see anything in my search history
down below, because I haven't made that part yet.
But if I go to my terminal, open that up.
Let me go back over here to my server.
I see that I ran this command, select star from history,
and it gave me this list of dictionaries.
So see, I have one dictionary that has ID and page as keys.
And the ID is one, the page is 10.
I have this next dictionary where the ID is two and the page is 50.
That's another row in my table.
And together, this is an entire list-- a list of rows.
So what could I do with this?
How could I render this in my HTML?
Well, as we saw maybe a little bit briefly in lecture,
I could try to pass in this variable-- rows--
to my HTML, and render it there.
So I could say, maybe I have this placeholder called history in my HTML.
And I'll make sure that that placeholder is filled with the values of rows.
And let me go over here to my index.html.
Now, open this part up.
And maybe in my table body, well, I could just place history like this.
I'll make this placeholder for history and I'll
put the value of rows right in there in my table.
So I'll go back to Library of Babel and I will hit slash to refresh the page.
And well I don't quite see what I expected.
This is not an ideal format.
I've kind of literally interpolated or copy pasted the value of rows
into my HTML, but it looks kind of wonky and not how I want it to look.
So there's a better way to do this.
And actually, in Jinja, instead of just pasting an entire variable,
we could iterate over a variable but in our HTML.
And now Jinja has the special syntax we can use to use for loops,
so what we have in Python.
So I could say this.
I could say percent-- or brace percent and then closing brace--
closing percent and then brace.
Let's say for maybe row in history.
And then end for down here.
So now I've made this for loop in my HTML
where history is the list I'm iterating over
and row is what I want to call everything--
every individual thing I'm iterating over on a given iteration.
So now I can say, I want to, inside of this table,
make myself a new table row for every row that I have in history.
And then inside that table row, I want to have some data.
Again, this tr is our table row tag and this td is our table data tag.
And now what should go inside here?
Well, maybe I could actually have this placeholder that is equivalent to row
and I could borrow some Python syntax here, row bracket page.
So what do you think we'll see in this case?
Any ideas in the chat if we do this?
We're iterating over our history list, a list of rows from our table.
We're going to call every iteration, row.
And then we'll say, row, get me the key--
give me the value corresponding to the key called page.
So again, here is our entire list, down below here.
On the first iteration, this will be row, and we'll get the page value--
so 10.
On the next iteration, this will be row, and then we'll
get the page value, or 50.
So let's refresh the page and see what happens now.
I'll go over here--
oops.
Here.
Slash.
And now, I do see my search history, kind of as I intended it to be.
And if I even go up here and type going to page 100, I'll go to that page,
scroll down, and I see that added to my history.
I could go to 100 again.
I see that same random string like we saw before,
and now I can see my search history down below.
So all of this, this kind of syntax here,
is just a way to print our list of dictionaries
a little more prettily, a little more in line
with what we expect to see in our HTML.
OK.
So what questions are there on this?
On how we passed in some list of dictionaries to our code
and iterated over it to make a table here?
Same question.
Showing the back end to the front end is not insecure, how do we hide that?
So the key thing to remember here is that this app.py
is kind of like what we call our back end-- something the user never
really sees.
And even our HTML page over here is somewhat back end, in the sense
that if I were to render this--
let me look over here.
If I were to render template, index.html,
well, I would actually see not this code for row and history
and for row, bracket, page, I'd actually generate this HTML file
and then show it to the user.
And so if I go to my HTML file over here and scroll down,
notice how I don't actually see any of the Jinja syntax,
I just see the resulting pieces of it here.
So I don't see for or the dictionary or anything like that.
I just see the result of it here.
And so that is secure enough for us, as we're
showing our web page to the user on the front end, what they can see up front
here.
Question-- how do we get the key values?
So notice in our HTML.
We said row with the key of page, but how do we know it was page?
Well, if I go to my SQL prompt over here, select star from history
as you did before.
I have this row called page.
So it's just in the way we made our table we had this column called page
on a row, a column called page here.
OK.
Other questions here?
While we're waiting for questions to come in,
this week will be a lot of doing exactly what we did today,
of trying to insert some data from Python into HTML,
trying to render dictionaries, lists of dictionaries
and so on, and even using some of our helper functions
that we saw before here.
I hope you go off and work on this problem set with some of what
you learned during this section.
We'll probably officially conclude here, but I'm
happy to be around to answer any questions that you all have.
Thank you very much for coming and I hope to see you next time.