Okay, this is the unit 4 homework solution.
To recap, the first thing I wanted to do is make a registration page.
This should look basically like the homework 2, where we made the form.
So if you type in some junk, we get the same error messages,
and if you type in a valid username and password, and you submit,
you get redirected to this welcome page, that says, welcome and then has the username in it,
and the username this time is not included as a get parameter.
It's actually included in a cookie,
and I'm using this little cookie extension in Chrome so I can see the cookies,
and in this case, you can see it included.
This is supposed to be the user id 5.
That is the hash of that as we discuss that technique in the lesson.
So let's look at the code for the first problem.
Once you solve this first registration part of the homework,
the other 2 parts of the problem are very easy because they just kind of build
on the same code, so we're going to be going through a lot of code here for this first part.
The first thing I have is I have a secret, which is just this random string
that we're going to use as our hash secret for cookies.
Normally I wouldn't store this in the same file as my code,
I'd probably store it in some other module that's only on the production machines
or something like that,
but for our purposes, it's fine just to stick it in here.
Again, a lot of this code you've seen before, template directory stuff.
Here are 2 functions.
You basically had quiz problems in the lesson for generating these functions
for making a secure val, which takes a val and just returns that value--a pipe,
and then the hmac of that val.
We did this in the lesson, so you should be familiar with this.
and then the function for taking one of those secure vals and making sure it's valid.
We split on the pipe.
If a call makes a secure val on the value we pulled out of the string,
if it matches that string itself, it's valid.
You've seen this functions.
We did them in quizzes and we're going to be using them in here.
Here is this bloghandler.
This is the class I've been using, kind of a parent class for all of my handlers.
I added some more functions.
Write, render, stern, render--those are old.
I've added a function called set
which basically sets a cookie whose name is name and whose value is val.
All it does is it basically gets the secure, calls make
and then it stores that in a cookie.
and we store a cookie by just using 'Set-Cookie' header,
and say name = val. Path = /.
We could also include an expires time, but I didn't here so by default these cookies
are going to expire when we close the browser, which is fine for our purposes.
Then we have another function for reading a secure cookie.
You give it a name, and if finds that cookie in the request.
If that cookie exists, and that cookie passes check
we return cookie_val,
In Python you can use this kind of shortcut notation for saying,
it's the equivalent of writing, if cookie
If 1 of these is false, we just return false or none or whatever.
We've got this function login.
This will be used for the second part of the homework for actually doing logins,
and this just sets the cookie,
and we've got a function logout, which I believe is the third homework question.
We'll come back to these.
I added this function initialize.
You don't strictly need this.
Google App Engine is a function that gets called before every request,
and so what I have it do is check for the user cookie, which was called user_id.
If it exists, store in self.user the actual user object, which we'll get to this in a second.
Now you don't have to do this, but I kind of did the more complicated route
where you have to login to enter things into the blog, and it keeps track of all the blog pages
where the user is and that sort of thing,
so if you're going to have that structure where once you're logged in every page
is a little bit different, it's handy to have some sort of function like this
that gets run on every request and just checks to see if the user is logged in or not.
That's what this initialize function does, and this function is called by the App Engine framework.
So we still have some of our blog stuff.
I've been doing all the homeworks on the same file,
so you'll see some of the other homeworks in here.
We have this function make_salt.
This was a quiz function, just to make a string of 5 letters.
So that's what this does. This makes our salt.
This function was also a quiz function, I believe, for making a password hash.
It takes a username and a password and an optional parameter for salt,
and then returns the salt, the hash version of the name, password, and salt.
This is what we store in the database.
Then we have this other function, also a quiz question from the lecture
of how to verify a password.
So it takes a name and a password and the value in the database,
and it makes sure the hash from the database matches the new hash created
based on what the user entered in.
Again, quiz question. You've seen that before.
Users_key--this creates the ancestor element in the database to store all of our users.
Again, you don't have to do this.
I like to have things a little bit organized,
so again, I gave it a parameter and thinking maybe I'll have user groups in the future.
I don't right now. That's just how I did it.
Okay, so now we get to some interesting code.
This is our user objects that we'll be storing in the database.
so it inherits from db_model that's what makes it a data store object.
and it has three parameters:
it has a name, which is a string property, required,
a password hash, which is a string property, also required,
and an e-mail, which is a string property, but not required.
Now, remember, we don't actually store passwords in the database,
we just store hash of the password. We can't grade that, we can't tell
how you're storing stuff in the database, well, we could, but you would have to
write some other handlers or something...
But this is how I'm doing it. And then I added a couple of functions
on this class. They are kind of
convenience functions. The syntax here, @classmethod
this is called a decorator. I'm not going to get into exactly what decorators
are, but what classmethod does, is it says: you can call this method, on this object.
It doesn't have to be instance of this object. So normally you would say the first parameter
on a method on a class itself, that's referring to yourself,
that instance. In this case I call it "cls" for "class", which means it's
referring to this class user, not an actual instance of a user, these are objects
So we can just say, user.byid, give it an ID, and then we'll call this get
to load the user on to the database. Get
If you recall from a previous homework, I had this kind of "make key" thing,
and just called the "get" function, but that was before I learned about this
get
I have another function called by_name, which looks up a user by its name.
I'm using the datastore procedural code again for doing the database look up,
instead of the GQL, because I prefer typing this, I think it's a little bit easier to read,
and all you do is, you say, filter(name =', and then you can
give it another parameter, so this basically says, "select * from user, where
name = name." We call "get" on that, which returns the first instance,
It returns that.
Another function I've added is this method called register, which takes
a name, a password, and and e-mail, and creates a new user object.
So the first thing it does is create a password hash for that name
and password, and then it creates a user object. The parent is that user key,
not required for you guys, name = name, pw
email = email. So that creates the user. It doesn't actually store it
in the database yet. It just creates the object.
And here's another one, called "login". We'll come back to this one, but
it's fairly self-explanatory, for the future homework.
So now, let's go down to the handlers. You can see I've added three handlers
for each of these three homework problems. The first one we'll talk about is "signup",
so /signup goes to the register handler, let's take a look at that.
Now, the register handler here inherits from the class "Signup".
"Signup" is actually up here, and it's identical to what I had
or, nearly identical to what I had for the homework 2. Since I'm
working in the same file I didn't want to rewrite this whole class again.
We are still rendering signup form, like we were before. We are still,
I'm not going to go through this in great detail, but we still get all the values
out of the request, we still check to see if they are all valid, and
if there is an error, we re-render the form, with the error messages and the values,
just like we did before, and if there's not an error, I call self.done,
which in this class "Signup", doesn't do anything, it just raises an error.
And then I have two other classes that inherit from "Signup" and overwrite this function
"done". The first one is the Unit 2 "Signup" page, and all that one does is, it redirects this
/unit2welcome with the "get" parameter. That's how we did the Unit 2 homework.
The "register" one overwrites "done" to have different behavior.
First, what it does, there's an extra error that can occur in this one,
in that the user can already exist, so what I do is I look up to see,
"does that username already exist?" And if it does, send an error message
and I re-render that form. error_username = msg, so
I probably could have done this a little bit more cleanly, but this is kind of just
tax on an extra message, so this doesn't appear to preserve the parameters
if you enter in a different username. It's a little bit less than ideal, I probably
could have put some more things in there, but that's how we have it.
Otherwise, if that error doesn't exist we register the user, which creates
a user object, and we actually store it in the database here.
And then, what I do is, I call my "Login" function, all the "Login" function does is
set the cookies. So, for the first homework we may not have a "Login" function,
you may actually just set the cookie here, and then we redirect it to the welcome page.
But then I generalized it, when I did the "Login" version, and just made this work,
this "login" function that multiple handlers can use, because we will have both the
Sign up page for registering and signing in, and then we'll have just the "Login page",
not for creating a new user, but signing into an old one, and they can both use this
"Login" function, and we'll go over that in a second.
We redirect to /unit3welcome, which is down here, this handler,
Unit3Welcome, and all this does is, it checks to see if self.user, and
remember self.user gets set up in that initialize function, that's where it
reads the cookie and makes sure that the cookie is valid and sets the user
on the handler, and since this Unit3Welcome inherits from BlogHandler,
it has access to that user, we render our Welcome HTML, you can probably
guess what's in there. With username = self.user.name,
Otherwise, we redirect to the Signup.
Let's see that working one more time, again, now that you've seen the code.
Okay, so if we enter a username and a password, we should actually
get an error message that this username already exists. And we do.
And it cleared the form, because I was a little bit lazy on this one,
and didn't preserve all those parameters. Although I easily could have, so,
let's type a new username, and submit the form, and it worked!
And if I were to, on this page, modify this cookie, change it to
user id 5, which is what we had before, and I were to reload this page,
we get redirected to the signup page, because the value in that cookie
doesn't match the hash in that cookie, therefore is invalid, and we
get bounced to the signup page. So that is the first question in this homework.