Introduction to Databases and the Project Goal
Hello everybody and welcome back to the Flask tutorial. So in this video, what we're going to be doing is talking about databases and how we can actually save user-specific information. Now, I know we haven't gone into user authentication yet and talked about password hashing and all of that, and we're not going to do that in this specific tutorial or probably the next one either. But if you guys do want to learn how to do that, let me know, and maybe I'll make a video on that later. But right now what we're going to do is just work on saving information in a database, understand how that works, creating some database models. This will probably take, you know, two videos, maybe three, depending on how much we end up doing. So if I don't get to everything in this video, don't worry, it's coming in the next one. That one should be out soon.
Alright, so let's go ahead and get started. So what I want to actually do is make it so that when a user logs in, they're brought to a page that's almost kind of like a profile page, like a very simple profile page where they can modify some information about themselves. Now to keep things simple, we're just going to make that information an email. So each user has an email. When they go there, they can change their email, they can update it, they can delete the email, they can do whatever they want. And we'll save that in a database. And then the next time that the user logs in, we'll look for that email and we'll display it, and then they can change it just to give you an idea of how we have persistent information. Now we're going to do this using something called SQLAlchemy.
Installing and Importing Flask-SQLAlchemy
I believe that's how you say that. So we're going to start by actually just installing that. I gotta stop this server. So we're going to go to command prompt and just do a pip install flask-sqlalchemy, I believe is how you spell that. So I already have that installed. You guys run and enter on that. Again, flask-sqlalchemy and then we'll be good to go.
So we're going to start by just importing that. So we'll say import sqlalchemy.
which is going to be our import, and now we're actually just going to work on some of the front-end stuff. So getting that form set up, grabbing some information from the form with a post request, and then we'll get into the database.
So I'm going to go to this user.html file. You can see I've cleared out what we had previously that showed, you know, welcome user, because what I'm going to put in here now is just a form that shows the user's email. So to do that, we've done this before. We're going to say <form>, I'm going to say action equals just a pound sign. And then we'll say method equals post, like that. End our form. And then we'll put our input. So we're going to put—did not mean to do that. We'll say <input type="email"> like that. We're going to need a value, which we'll do in a second, but we also need a name. So say name equals email. We're going to say, what is it, placeholder equals in this case, which is going to be, what's our placeholder be? Enter email. And then we're going to have a value which is going to be equal to—this is going to be kind of weird because we'll see how this works in a second—email if email.
Now, the reason I'm doing this is because we're going to pass a variable to this field, or to this template, that's called email. So I'm going to check if email has a value, and if it does, we're going to display that as the value for this email rather than displaying the placeholder of enter email. So only the first time before the user's typed anything in, which will be the placeholder, otherwise, we're going to show the actual value of their email that we grabbed from the database, which we'll do later.
So now let's make a submit button. So say <input type="submit"> like that, and we'll say value equals the same thing which is submit. And I apologize for the sloppy typing. All right, so there we go. We have our form in here.
Now, what else did I want to do? Oh, I've actually already added this, but let's just go over it. In my base template, now I've just added this thing that says <div class="container-fluid">. This is a Bootstrap class, and I've just put my block content in there so it gets off the left wall of our website just a little bit. It just is a fluid container that covers the entire web page essentially. I'm not going to explain really what that is because I'm not a pro on it, but it just makes it look a little bit nicer.
Implementing Session-Based Data Storage
Okay, so now that we've done that, what I'm going to do is actually start doing some stuff with sessions and saving the user's email in a session. And then once we have it in a session, we can kind of compare the difference between a session and a database. And since we already know about sessions, we'll get started with that.
So the first thing I'm going to do is change my user page here to have a methods. So it's going to have methods, I believe it's like that, and we're going to have POST and GET, just like our login page before. And now what I'm going to do is actually inside my user function, I'm going to start setting up a bit of code, and I'll walk through it after. So I'm going to start by saying, oops, not in all capitals and spelled correctly, email = None. And now what I'm going to do is check the current method if the user is in session. So essentially, if we have a user, if we've logged in, now I'm going to check what method we reached this page with, if it was a post or if it was a get. So I'm going to say if, I believe this is going to be a request.method == "POST", then what I'm going to do is grab the email from that email field. So to do that, we're going to say email = request.form, and then we remember we just use this key, so we say email like that. Awesome. So grab that email. And then what I'm going to do is actually store this in a session. So I'm going to say session, we'll use the same key, as in email, equals email like that. Okay, awesome.
So we're actually going to get rid of user=user here. What I'm going to just do is say email=email. So now essentially what we're going to do is we're going to pass in an email here and display that. Now what am I going to say though, is if the request.method is not POST, so if it's a GET request, what I'm going to do is get the email from the session. So I'm going to say email = session['email'] like that. And we'll start by saying if the key email is in session. Now since we've already defined email = None up here, we shouldn't have any issues passing this email in. And essentially, if we pass a None email, then we just won't display anything, and we'll have that kind of template text that we talked about before, that placeholder text.
Testing Session Persistence & Cleanup
Okay, so that's a bit about it for the session. I'm just going to pop the session when we log out too. So session.pop('email', None) just to make sure we remove that when the user logs out so no one else sees that or the user doesn't see it when they log back in. And yeah, that should be about it. So let's test this and we'll get into the database stuff. Okay, so I'm going to run this. python tutorial7.py. I've already got this up here. Let's refresh. You are not logged in.
Alright, so let's go Tim, submit. There we go, login successful. Okay, so I've just entered my email here. I just had to fast forward through that because I didn't want you guys to see all my emails because it was showing it. So now if I click submit, you can see that it submits that and our email is saved. Now we should probably show some message to the user to tell them that their email's saved. So let's actually do that. And flash a message in here. So when they hit that submit button on the post request, we'll say flash and we'll say "Email was saved", like that. Okay, awesome.
So let's go back in here and actually change this now. Let's just change that to ca, submit. We get "Email was saved." Okay, so let's go to the homepage and make sure that our session was working. So go to the homepage, go back to /login. You can see that it still has this saved, [email protected]. And then obviously if we close the web browser and this wasn't a permanent session, this will go away and it won't be saved. So now it's time to talk about databases.
Configuring the SQLAlchemy Database in Flask
Okay, so we've done all that, that's all great. And now let's start setting up a database. You start by changing this import statement because I just realized this is wrong. What our import really should be, and I wasn't even close, is from flask_sqlalchemy import SQLAlchemy. Now what we're going to do is set up a database object, which is going to be equal to a new kind of SQL database. So to do this we're going to say SQLAlchemy and then inside of here, what we're going to do is actually just put the parameter app.
Now we're going to set up a few kind of configuration properties here for our app to define some stuff to do with the database. So to do that, we're going to say app.config. And then here, I'm going to have to copy this in because I don't want to mess it up. I'm actually... I'll just copy the entire line, then you guys can see it. So I'm gonna say app.config['SQLALCHEMY_DATABASE_URI']. Notice that's not an L, that's an I. Equals sqlite:///users.sqlite3. Now this right here is actually going to be the name of the table that you're going to be referencing, which we'll talk about in a second. But for now, we're going to leave this as users because that's what we're going to use, and then again, sqlite3. So make sure you have that.
I'm going to copy in one more thing here. This is optional, but this is just going to remove a warning that we get sometimes. So SQLALCHEMY_TRACK_MODIFICATIONS = False. What this is going to do is make it so we're not tracking all the modifications to the database, I believe that's correct. And this just pops up and tells you, you know, add this if you don't want a warning, so I figured we'll add it now so we don't see that popping up.
Okay, so now that we've done that, we've actually set up the kind of database configuration and now it's time to create what we call a model which we're going to store information in.
Defining a Database Model for Users
So the reason why I'm showing you guys this SQL Alchemy is because it makes it way easier to save information because we can write all our database stuff in Python code rather than writing SQL queries. So some of you may be familiar with SQL, maybe you've used SQLite 3 before. This is going to be a little bit different syntax, but you should hopefully be able to understand it.
So any kind of object that we want to represent or any pieces of information can be stored in rows and columns in our database. Now, the columns are going to represent pieces of information and rows are going to represent individual items. Now, in our case, we want to store users. And our users are going to have, in this case, just a name and an email, and that's all we want to store. So what I'm going to do is actually define a class which is going to represent this user object in our database. And it's going to be called users. Now, what I'm going to do in here—I think I'm going to actually look at this because I forget what it is—is say db.Model as the inheritance, which means this is actually going to be a database model, which means it has a few methods and things that are inherited from it. Now, what we actually do to define the properties that we're going to save is write them as class attributes. Now, this is pretty straightforward. You can pretty much copy kind of the lines I'm doing and tweak their names if you want to store more or different information. But the first thing I'm going to do is say _id equals, and in this case, we're going to say db.Column which again is representing the type of information. And then here, we're going to put what this is going to be. So we're going to say db.Integer like this, which is going to be the type of information. We're going to put the name actually before that which is going to be ID, and then what we're gonna do is say primary_key=True.
Now what I've done here, and this is a little bit confusing, so I'm going to walk you through it in case you don't know what SQL is or how this works, is every single object that we have in our database needs to have a unique identification. Now that identification could be a string, it could be a boolean so we only have two values, it could be an integer, it could be whatever it wants, but it needs to be unique. So what I'm doing here is saying that each object in our model is going to have an ID, it's going to be an integer ID, and a primary key equals true, which means that this is the way that we're going to reference all these objects. And all of these, every single one of these keys needs to be unique in our database, or in our table. So in this users table is what we call it, every single row has to have a different ID. And that's so that we can, like, tell the difference between two different objects. Because if we don't have that, then technically we could add duplicate elements, which will result in errors when we're trying to grab elements from the table because we'll have two of the exactly the same thing, so we don't know which one to return. Right?
Okay, so I've explained that. Hopefully that makes sense. If you have questions, leave a comment. But now what we're going to do is define our name column. So we're going to say name = db.Column(db.String(100)). And I believe that's actually all we need. And then we're going to say what was the last one I wanted: email = db.Column(db.String(100)). Okay, so let me check to make sure I did that correct. I did. Awesome, awesome, awesome. Actually, we don't even need to define these names here. Because if I don't define the name, then what's actually going to happen is it will just use this variable name, which is fine, that's all we need to do. So we just need to define the type here. So if you wanted to change this and add more attributes, all you would do is literally just take this line, email, paste it. You could change this to an integer, string, I believe there's floats, booleans. They have some other values as well, but you guys will have to look those up if you want the specific ones, I'm not going to walk through all of them. And then that'll say this is a new column in our database and we can store that information for each user.
Initializing the Model and Creating the Database
Okay, so now we're going to define an init method. And this init method is going to take the variables that we need to create a new object because technically we can store some values in here that are going to be None values or null values, which means that for some objects, they might not actually have a value for that property. For example, say we have, you know, gender as an option and, you know, now some people decide not to declare that. So maybe we leave that as None, and then for some people we have male and we have female, but some people just don't have anything for that column.
So what I'm going to do here now is say name and email because these are the two things that we need every time we define a new user object. This ID will actually be automatically created for us if we don't do that because it's a primary key. And now what I'm going to say is just self.name = name and self.email = email.
Okay, so I've tried to explain this model. Hopefully this makes sense. Again, you guys can always look up the documentation and read through all this if there's anything that I skimmed through or don't go into enough detail. But okay, so now that we've done that, what I'm going to do at the bottom of my program is add something that we need, which is db.create_all(). Now, what this is going to do is actually create this database if it doesn't already exist in our program whenever we run this application. So that's important. Make sure you put that above the app.run() so that the app doesn't have an issue when, you know, that database doesn't exist and it starts running. And with that, we can actually start saving users.