BRIAN: All right.
So far, we've been able to use
Flask to build web applications.
But so far, the web
applications we've been creating
are serving the same
files to every user.
But a lot of web
applications that you might
have interacted with in social
media websites or other websites
let you log in.
You log in once-- and then every
time you visit the website again,
it somehow remembers who you are.
You're not prompted to
re-log in every time.
It knows who you are so it
can to display your news feed
or information that's relevant to
you when you visit that web page.
How is it that websites
are able to do that?
Well, it turns out
that many websites use
a technology called cookies,
where cookies are just
data that a website can ask your web
browser to store on your computer
such that it's stored in your computer.
And the next time that your web
browser visits that same website,
your web browser can show
the cookie to the website
so that the website
knows who you are again.
It's an identifying piece of information
such that after you log in once,
some cookie is stored
inside your web browser.
And the next time you
visit that same website,
you can present that
cookie to the web server
so the web server knows who you are and
knows what information to then display
to you.
This allows for the notion of what
we're going to call sessions--
some data about your current interaction
with a website that is specific to you.
And so let's take a look at how we can
add sessions to our web application
now.
So what we're looking
at here in CS50 IDE
is the same task management
application that we created earlier
when we were creating our first
web applications using Flask,
where we would create an application
that displayed a list of the things
that you need to do.
And then give you a page where you could
add a new task to that list of things
that you need to do.
Let's take a look at how this
application works, just to recall.
So here's our page with all our tasks.
And if we create a new task, just
call it foo, and press Submit.
I'll create another task,
call it bar, and Submit.
All right, I have two tasks now.
But the interesting thing about this
is that currently this data is just
stored as a global variable
inside of our Flask application,
which means that it's shared among all
of the people that might conceivably
be using this application.
If another person were to
open up this page, which
I can simulate by opening this up
in an incognito window, for example,
taking the same URL, pasting
it in, the other user
is also going to see that they
have two tasks, foo and bar.
And if this other user
here on the right creates
a new task called baz and submits
it, then the user on the left
will also see the task baz if
they are to refresh that page.
And that's probably not what we want
for some sort of application that
allows users to maintain their
own lists of tasks, for example.
Ideally, I don't want the list
of tasks to be a global variable.
I wanted data to be data that
is local to someone's session.
That when I log in, I
should see my tasks.
And when I'm interacting
with the page, I
should be able to add
to my list of tasks.
But that shouldn't affect someone
else who has their own list of tasks
that they're maintaining on
the same web application.
So let's now see if we can implement
this by introducing sessions
into our web application.
There are a couple of setup
things that I'll need to do.
From Flask, I'll need to
import session, lowercase s.
And I'm also going to use a
library called Flask Session, where
I'm going to import uppercase S
Session, which will just give me access
to some additional features.
After here, there's some
configuration steps that I need to do.
No need to worry too much
about the details about this.
But in short, what I'm going to
say is that session permanent
is going to be false.
I don't want sessions to be permanent.
And then I'm going to add a
session type equals file system
to mean the location where I want
to store all of the data pertaining
to sessions is going to be any
file system of the web server
that I'm running this application
from, which in this case is CS50 IDE.
And then the final
configuration step is just
to say capital S session,
in parentheses, app,
to mean that I would like to enable
sessions for this particular Flask web
application.
So what does this mean?
What this means is that I'm now going
to have access to a Python dictionary.
Recalling that a data dictionary is
just a collection of key value pairs
called session where I
can access information
that is going to be local to the user's
current interaction with the web page.
And it's not going to be dependent
upon what other users have also
added to their own lists of tasks.
What that means is that I can
get rid of this list, which
I'm going to call todos.
And instead, inside
of tasks, we're going
to use a key within
the session dictionary.
So what I'm going to say inside
of tasks is this first line.
I'm going to check, is
there already a key called--
we'll call it todos
inside of the session.
And in particular, if todos
is not already in the session,
well, then I want to
add it to the session.
So then I want to say session todos,
it's just going to be the empty list.
So what are these two lines doing?
Well, these two lines are
saying, for the current user,
do they already have a key called
todos inside the current user's session
dictionary.
If they don't, then we're going to
create a new key inside of the session
dictionary called todos them and
set it equal to the empty list.
Meaning initially, the user has
no tasks that are currently inside
of their list of tasks.
All right, let's move on.
Now in tasks.html, rather
than passing in todos
as just a variable todos, which was a
global variable that we no longer have,
now we're going to say the list
of things to do is going to be
session, square bracket, todos.
In other words, for the current user
session, the current user's interaction
with this website, let's get
at their list of things to do.
So that's the function that's going
to display the current list of tasks.
But now let's go down
here to the add function.
What needs to change here?
Well, we've gotten the
current task, the thing to do,
based on whatever was
passed into that form.
Request.form.get task means get whatever
the user typed in to that task field.
And now what we'd like to do is add
it to the current users list of things
that they need to do.
So here we have todos.append
todo, where before todos was
a global variable,
accessible by all users who
were using this web application.
But now what I'd like to do is make
this local to the current user.
And so the way I can do that
is, instead of saying todos,
I'll say session todos, to mean,
take the current user session,
access their list of things
to do, and add to that list
this new thing that I need to do.
So instead of using a
global variable, I'm
now using part of the
session dictionary in order
to include and store data that is
relevant to the current user only.
Let's try this out.
I'll run the web application
by running Flask Run.
And now if we take a look at the web
application, loading it in both pages
for two different users.
Now if user number 1 creates a new
task called foo and then creates
a new task called bar, they
still see their own tasks.
But if user 2, if I refresh the page,
they don't see those same tasks.
User 2 can now create a
new task, baz, for example.
And now they have their own separate
list of things that they need to do.
Because this data is no longer
being stored as a global variable.
It's instead being stored in
each user's individual session.
And presumably Flask is
asking the web browsers here
to store some sort of cookie, some
unique identifying information
such that the next time you go to this
page, that's by reloading the page,
Flask still knows who the user is and
can display that user's list of tasks
back to them when they try and view
this particular route on the web
application.
All right, so taking
advantage of sessions,
we now have the ability
to store information
that just pertains to the current user.
But as we start to think about
users interacting with our web page,
you might imagine that our
web applications are going
to need to store more and more data.
We're going to need to
store data about the users,
any information relevant to those users.
And that's going to start to become
a lot of information to store.
And there are many ways
you could store data.
You could just always
store data in the sessions.
But the sessions aren't permanent.
And so eventually you might
lose that information.
You could store data inside of a file.
But then you need to know how
to open and read that file
and write to that file all the time.
But this is a great opportunity to take
advantage of something we've already
taken a look at, which are databases,
using a language like SQL that
lets us interact with a SQLite
database, for example, in order
to have tables of data, to insert
rows into that table of data,
to update those rows, and to
select rows out of that table such
that we can write a web application
that connects to a database
and that uses the data
inside of that database
to display information to people
that are using the web application.
And so that's what we'll do next.
Create a new web application
that we'll just say
is going to allow people
to register for something,
be it registering for a mailing
list or registering for a club
or registering for something else.
You can think of this as an application
that's going to store information.
And the information we're
going to need to store
is data about all of the registrants
for the event or for the club
or whatever it is that
people are registering for.
And what sort of information
might we want to store?
Well, we probably want to store
for each registrant their name
and maybe their email address as well.
And so let's give this a try.
I'll go back into CS50 IDE.
And now, let's go ahead and create a
new directory for a new application
that we're going to create.
We'll call the application register.
And I'll create a new file.
And this new file that I'm going to
create is going to be a database file.
I'll call it lecture.db.
So just create an empty SQLite database.
Now I'll go ahead and cd into register.
And now in order to access
this database, lecture.db,
I'll run SQLite3 lecture.db.
And so this now takes
me into SQLite where
I can run SQL queries on
this particular database.
And so what am I going to do?
Well, the first thing I need to do
inside this database is create a table.
So I'll go ahead and run some syntax.
So I'll say create table.
We'll call the table registrants.
And what are the columns that
this table needs to have?
Well, presumably, like many
tables, it should have an ID column
that will uniquely identify users.
So the ID column will be an integer.
And we'll say that it
will be the primary key.
It will be unique.
It'll be the way that we identify every
single registrant inside of this table.
And let's also add now
a field for their name.
Their name is presumably going
to have some sort of type.
And recall that we can
use varchar as the name
of a type of a column that store
some variable number of characters.
And we'll say we'll store
up to 255 characters, which
feels like a pretty
reasonable limit for a name.
And then we'll also store an
email address column, which
we'll also specify as varchar 255.
And so we've now created a
table that has three columns--
an ID column, which is an
integer, a name column, which
is a variable number of characters,
and an email column, which is also
a variable number of characters.
And here in the SQLite terminal, we
can actually try and select this data.
I can say, select star from registrants.
And of course nothing happens
because right now there
is no data inside of
this registrants table.
But let's add some data.
I'll insert into registrants
a name and an email address.
And what values do I want to insert?
Well, let's use the name
of Alice, for example,
and an email address
of alice@example.com.
So I've run a SQL query to insert
into the registrants table a new row.
I've inserted a row
with a name and an email
with the values of Alice
and alice@example.com.
And now if I go back and
select star from registrants,
selecting all of the data
from that table, we'll say,
we have Alice who's automatically
given an ID of 1, who has
an email address of alice@example.com.
So now that we've created
this database file,
lecture.db, which is a SQLite
database inside of which
is a table called registrants, let's
now create a web application that's
able to actually use that database
to read and write data to it
in order to display those registrants
and update the list of registrants
if someone new registers
using our web application.
So this is going to be
a Flask web application.
So the first thing we'll
do is create a new file.
And I'll call it application.py.
And again, this is where the Flask
application is going to be stored.
From Flask, import
Flask-- same as before.
This time, though, I'm also
going to add from CS50.
Let's import SQL because I want to
use CS50's SQL library, because I
want my Flask application
to now have the ability
to interact with the database
that I'm about to create.
So beneath that we'll say app
equals Flask name, same as before.
And now I want to give my web
application access to this database,
lecture.db.
So I'm going to say db, for database,
equals, and then SQL, and then SQLite,
:///, three slashes, specifying that
this is going to be a SQLite database.
And then what is the name
of the database file?
Well, the name of the
database file is lecture.db.
And now db is going to give
me access to lecture.db.
And it's going to allow
me to run SQL queries,
be they selects or inserts or updates,
that then are able to get at data
and modify data inside of
the lecture.db database.
Next, let's create a route.
I'll create app.route/.
We'll call the route index.
And what I'd like for the route to do
is just display to me all of the people
that are currently registered, based
on the data inside of my database.
How might I go about doing that?
Well, in order to get
the data, I'm going
to need to run a query
on the SQL database.
To do that, I can run db.execute.
And then in parentheses
and quotation marks,
the name of the query
that I now want to run.
In this case, it'll be
select star from registrants,
because I'd like to select all of
the data from the registrants table.
When I select all that data, the
data is going to come back to me.
And I'm going to want to store that
data inside of a variable, which
we'll call rows, in this case.
So I'm making a query on the database,
running select start from registrants,
which is a SQL query, taking
the result, and saving it inside
of this Python variable called rows.
What do I do now?
Well, now I'd like to take this variable
rows and pass it into a template
so that I can render the information
about who is currently registered.
Let's try that.
So I'll say return render template.
We'll call the template index.html,
which doesn't yet exist,
but we'll create it in just a moment.
And say that index.html is going to have
access to a variable called rows, which
will be equal to rows, the result of
whatever came back from the SQL query
that we ran on line 10.
I'll also need to import render
template in order to make this work.
But let's now create a new folder
called templates inside of which we'll
create a new file called index.html.
And index.html will be the template
that we're going to render.
So DOCTYPE HTML, HTML, head.
We'll go ahead and give this a
title and call it registrants.
And inside the body
of the page now, I'll
make a big heading at the top
that just says registrants.
But now what I'd like is a
list of all of the people
that are currently registered
according to my database.
How am I going to do that?
Well, recall that UL can be used
to create an unordered list.
And inside of that UL,
I'd like to loop over
all of the rows that got passed
into my index.html template.
So I'll say, for row in
rows, and then endfor.
Recalling that this syntax,
a curly brace percent sign,
is how I can add a
loop into my template.
Say loop over all of the rows
that got passed into index.html.
And for each one of those rows,
what HTML do I want to add?
Well, I want to add an LI, a list item,
that's going to show up in this list.
And I'd like to here plug in
the name and the email address
of every person who's registered.
And so every row that
comes back to me is
going to be in the format
of a Python dictionary,
where the keys are going to be
the names of all of the columns--
ID, name, and email.
And I can access the
values that those keys
to get at the person's name and the
person's email address, for example.
And so rows with all of the rows that
came back from my registrants table,
each individual row is just one row that
comes back from my registrants table.
And if I want the name that
corresponds with that row,
well, I'm going to print out as by
using double curly braces to substitute
a value here into the template--
row, and then in square brackets, name.
Say take the current row, get at the
person's name, and plug in that value
here.
I may also want to include
their email address, which I'll
do in parentheses for good measure.
And then I'll add another
set of curly braces
and go ahead and substitute in
row, square bracket, email as well.
So now I'm showing a list item
for every row in my table,
where I'm saying the row
name, and then the row email.
Let's now try to run
this web application.
I'll go down into the terminal and
run Flask Run to run the application.
And now if I try and load the
page, I now see registrants.
And now I see a list--
Alice, and then alice@example.com
as the email address
of the person who's registered.
And so if we ever were
to update this database,
changing the data inside the
database, this list would also update.
And we can see that if we go back here.
I'll go ahead and type in SQLite3
lecture.db to go back to the database.
And let me insert into
registrants a new name and email.
This time, what values
am I going to insert?
We'll add Bob and then bob@example.com.
And now if I exit out of the
database and run Flask Run,
and I reload this web page,
well, now I have two registrants.
I have Alice and I have Bob.
So now we have the ability to
design a web application that's
able to read data from a database.
We have a SQLite database.
And our route now is reading
data from that database
and displaying the information in the
database in the form of an HTML list
here.
Let's now add to this and actually
build into our web application
the ability to register
as well, the ability
to add data to the registrants table
instead of just reading data from it.
Let's give that a try.
Inside of index.html, we'll
go ahead and add a link
at the bottom that will
give me a way to register.
It will be a link that'll take
me to /register, for example,
which is a route that doesn't
yet exist, but we'll create it.
And the link will just say register.
What should this route do?
Here's app.route/register.
We'll create a function called register.
And for now, let's just go ahead and
render a template, register.html.
Let's now create register.html.
So I'll create a new file,
call it register.html,
which for now is going to include the
same information that was currently
in index.html.
And I could improve the design of this
somewhat by factoring out information
into a layout.html file, the
way I have previously as well.
So definitely something to consider too.
But here for now, what
I really want to do
is inside of the body I'll
call the title register.
And here I'd like to create a form,
a form that has an input, whose
type is text, and whose name is name.
I want the person to type in their name.
And so that they know that, I'll
add a placeholder that says name.
And I'll also have an input whose
type is text and whose name is email.
And just so the user knows that, I'll
add a placeholder for the input field
that says email address.
And then I'll have an
input whose type is
submit, which will give
the user the ability
to submit this form after
they've typed in their name
and typed in their email address.
What would I like this form to do?
Well, the form's action,
when you submit the form,
we'll go ahead and also
go to the /register route.
But again, I'm submitting data.
And so oftentimes when I'm doing this,
I want to add a request method of post
to say that I would like to submit
data via post to my /register route.
So what does that mean we need to do
with our /register route here inside
of application.py?
Well, first and foremost,
it means we need
to accept multiple requests methods.
We need to not just
allow for get requests,
but also allow for post requests
to be made to /register as well.
If the request method is get, then
we're going to return register.html.
And I'll go ahead an import
request up here as well so
that we have access to the request.
Otherwise, though, else, if
the request method is post,
that means the user has
tried to submit this form.
So what am I going to do?
Well, I'll say name equals
request.form.get name,
and email equals request.form.get email.
In other words, if the user
submits this form via post,
then I want to look at the information
that was submitted inside of that form
and access the user's name
and the user's email address,
storing them in these
values, name and email.
Then what would I like to
do with that information?
Well, I'm going to run
a query that is going
to insert this data into the database
to indicate that this user is now
registered.
But what does that query look like?
Well, it'll be db.execute.
And then what's the query for inserting?
It'll be insert into registrants.
What columns am I going to add?
Well, I'm going to add a name
and an email, and then values.
And then what I'd like to do is
provide a name and an email here.
Now you could try and do some
sort of string concatenation
to try and plug in the
value of the variable name
and the value the variable email.
But you have to be careful about that
because you potentially open yourself
up to SQL injection
attacks if you're literally
including strings inside of the query.
Whoever called it a
SQL injection attack is
where if name or email happens to
include some SQL-looking syntax, then
you might end up executing
some dangerous SQL code
inside of your database.
So just to be safe, we're going to
use placeholders here by saying :name
and :email to say we're going
to plug in some name here,
and we're going to plug in
some value of an email here.
And what values are we going to plug in?
Well, we're going to plug in a
value for name, which is just going
to be equal to name, the variable name.
And for the value email,
we're going to plug
in the value of the variable email.
So again here, we're just defining these
placeholders called name and email,
where we're saying the value
of the name placeholder,
we should substitute with whatever
the variable name is equal to.
And same thing for email.
In this case they just happen
to have the same names.
So I've inserted into the registrants
table a new pair of name and email.
And the last thing I'll do is I'll
go ahead and redirect the user back
to the /route, that default route
that determines what should be shown,
which is all of the lists of
all the current registrants.
Up at the top, to make this work, I'll
go ahead and import redirect as well,
since I want to be able to redirect
the user after this happens.
Let's give this web application a try.
I'll run Flask Run.
And now I'll reload the page.
And when the page reloads, I now
see that Alice and Bob are still
registered.
They're still inside the database.
But I now see this additional
link Register at the bottom.
When I click the Register link, I'm
taken to the /register route on this
web application where I'm
now presented with a form,
somewhere where I can type in a name
and an email address, and then submit.
And the placeholders that I included
as attributes of these input fields
are now showing up to me
so that I know whether I
should type in my name or the email
address into which of these two fields.
So we'll go ahead and type in
Charlie and then charlie@example.com.
And I'll click Submit.
And OK, I get redirected
back to the Registrants page.
And now Charlie is a registrant as well.
And I can confirm this
if I go back, close out
of the application, and
type SQLite lecture.db
and select star from registrants.
I now have three registrants.
You can't see Charlie's
full email address.
But we do see that we have Alice,
Bob, and Charlie all inside
of this registrants table.
Let's put a few finishing
touches on this web application.
First and foremost, as
we mentioned before,
there was some overlap between
index.html and register.html.
So I'll first create a new
file called layout.html,
which will contain
all of the HTML that's
in common between the two HTML files.
So I'll take register.html,
paste it in here for now.
And really the only thing that's
going to differ between the two pages
is the contents of block body.
So I'll create a body block
we're now inside of index.html,
instead of all of this, I can just
say we're going to extend layout.html.
And then inside of block
body is going to be the list
of all the current registrants.
And then I'll end the block after that.
So, simplifying the
code just a little bit.
And I'll do the same thing in
register.html where instead of all this
I'll just say we're going
to extend layout.html.
And inside of block body, we
have register up at the top,
and then the form where the user
can actually register here too.
So this shouldn't actually change the
way the application behaves, but just
cleans up the code a little bit.
In case we want to make
modifications to the layout,
it will affect all of
the different HTML pages.
But let's now actually do some changing
of the logic of our application.
Because one thing we
haven't yet accounted
for is the possibility that maybe
the user didn't type in their name
correctly or didn't type
in an email correctly.
And one of these two fields
is blank, for example.
What might we do then?
Well, let's create an
apology.html page, which will just
be an error page where
we apologize to the user
if we can't perform the
requested action for some reason.
So I'll go ahead in templates
and create a new file, which
I'll call apology.html, inside of which
we'll go ahead and extend layout.html
again.
And then inside of
block body, I'll display
in each one that just says sorry.
And then maybe we have some error
message of something that happens.
So inside of a div I'll display
the value of a variable called
message, which I haven't yet defined.
But we'll get there in a moment.
And then at the bottom, maybe a link
that takes me back home that just says
go back.
So this is an error
message page, something
that we're going to display if something
goes wrong in the web application,
for example.
And when might something go wrong?
Well, inside of application.py
we might find that maybe the user
didn't type in a name.
So I can say, all right, if not name--
in other words, if no
name was provided--
then what I want to do is return,
render template, apology.html,
passing in a message of,
you must provide a name.
And likewise, we can also add another
condition that says, if not email--
if the user doesn't type
in an email address--
then we should also return
the template, apology.html,
meaning some error took
place, with a message of you
must provide an email address.
Let's give this a try now.
I'll go ahead and run Flask Run.
I'll load the web page.
And I see my list of registrants.
And now let me try to register.
But let me leave both the name and
the email address fields blank.
I'll press Submit.
And we get a sorry message that
says, you must provide a name.
Let's go back.
We're taking back to the home page.
I'll try register again.
Let me type in a name.
I'll type in Dave.
Press Submit.
Sorry, you must provide
an email address.
All right, let's go back.
Register.
I'll type in Dave.
I'll type in dave@example.com.
I'll press Submit.
And now Dave was successfully
registered as well.
And I have this list
of all four registrants
that are currently inside
of my SQLite database
that I am reading from in order to
display them all as individual list
items inside of my HTML template.
So by including SQL queries
inside of our web applications,
we now have the ability to
write Flask applications that
interact with a database.
That are able to store
data inside of a database
and read data from
that database in order
to display that information dynamically
to users who are visiting our page.
And this really opens the door for a
wide variety of possible applications
that we can design.
Any application that
has multiple routes,
that store in some sort
of data, and that's
using that data to be presented
in templates, you can create just
by combining these ideas,
using HTML for the templates,
using Flask as the web server
that's listening for requests
in particular routes, and
responding with information,
and by using a database
like a SQLite database
in order to store information that
your web application needs access to.
This, with web programming.
