CS/COE 1520 Jarrett Billingsley Flask models CS/COE 1520 Jarrett Billingsley
Today: databases SQL crash course ORMs and SQLAlchemy Flask models
Databases
Motivation files are neat and all, but… when we're trying to represent large stores of data… file systems are hierarchical, which is rarely a good organization for data. the OS handles them, and imposes a lot of restrictions and overhead. Runtime OS API Syscall Handler Kernel FS Driver there are no guarantees that integrity (correctness) will be maintained. my super important data that I really don't want to lose oh no everything is going wrong files have no inherent structure.
data is loosely coupled and can be related in very flexible ways. What's a database? a way of storing data such that it can be efficiently searched and modified, while ensuring certain kinds of correctness. we'll focus on relational databases, where… the data is highly structured into rows (records) and columns (attributes). they can be implemented in ways that ensure that data is not lost, corrupted, or in an invalid state. id lname fname age 1 Brown Pat 19 2 Green Leslie 22 3 White Alex 20 id crn dept cls 1804 20259 CS 0449 1805 20261 1520 data is loosely coupled and can be related in very flexible ways. - god it's hard to come up with a sufficiently abstract definition that covers all the kinds of databases in use today
Tables relational DBMSes (database management systems) store data in 2D tables. columns represent data fields within each record. typically a table has a primary key (PK): a column which uniquely identifies each record. id lname fname age 1 Brown Pat 19 2 Green Leslie 22 3 White Alex 20 rows and columns can be added, deleted, and changed at will. each row is a record – a single entry in the table. you could think of a table as an array of class instances…
Relationships one table can refer to another's rows by using their PKs. in that case, they're called foreign keys (FKs). Students Students_Courses id lname fname age 1 Brown Pat 19 2 Green Leslie 22 3 White Alex 20 FKs to Students student_id course_id 1 1804 1805 2 3 FKs to Courses Courses id crn dept cls 1804 20259 CS 0449 1805 20261 1520 we could use this to look up all the students in a course… - this relationship would be impossible to represent hierarchically; it's just… not hierarchical at all. …or all the courses a student is enrolled in!
Cardinality Ratios when modeling data, objects can be related to each other in a few common ways. one-to-many (1:n) VALID THRU 11/3/2019 one-to-one (1:1) (a person has an ID, and each ID belongs to one person) (a person has many fingers, but those fingers belong to one person) many-to-many (m:n) CS1520 - of course there are modifications, like 1-to-0-or-1, 1-to-1-or-more-but-not-0, etc. (students can take several classes, and a class has several students)
Transactional databases databases are typically used by multiple clients. a single operation can have multiple steps. A B A.account -= $30 B.account += $30 what if this sequence of steps were interrupted in the middle? these steps should be atomic. - I mean, there's more to it than that, but that's the core of the idea. a transaction is an atomic sequence of database events.
ACID most relational databases use ACID transactions: Atomicity either all steps are completed, or none are. Consistency before and after the transaction, the database will be in a consistent state – that is, no conditions will be violated. like cardinality! Isolation the outcome of two concurrent transactions will appear as if they were executed serially… for some definition of "serially" Durability once completed, a transaction's changes are permanent, and will survive any failure. - Consistency: it's possible for the DB to be inconsistent in the middle of the transaction, but no one will be able to see that.
SQL (just a peekquel)
The languages… they never end… SQL (said "sequel", Structured Query Language) is… well, a structured language for querying databases. :B it is not a database, just a standardized language for accessing them it is a declarative language: you say what you want, and the DB decides what algorithms to use to give that to you. "gimme all the freshmen." which fields you want. select * from Students where year = 'Freshman' what table(s) to look at. a predicate to satisfy. a Python list comprehension might look like… - you'll see a lot (most?) of SQL written in ALL CAPS but it's too SCREAMY for me - it's not case-sensitive - stop screaming, SQL. [s for s in Students if s.year == 'Freshman']
Joins select count(*) from Students, Students_Courses where a join takes data from multiple tables and synthesizes new data. "how many students are in course 1804?" select count(*) from Students, Students_Courses where Students.id = Students_Courses.student_id and Students_Courses.course_id = 1804 this is an implicit inner join, which is like a set intersection. and that's all the SQL we'll talk about.
ORMs
But what if we don't wanna use SQL we've got all these object-oriented programming languages wouldn't it be nice if we could just… use our language's objects and have them automatically backed up to a database? an Object-Relational Mapping (ORM) maps your language's objects into a relational database. u = User("Bob") u.pass = "god" u.commit() insert into Users values ('Bob', 'god') not only does this avoid SQL, it makes your app more flexible – you can use any database backend you like. - there are ORMs (even multiple ORMs) for many languages - a nice layer of abstraction makes things more flexible but less performant… - if you need performance, you'd have to dip into the database's native API (either SQL or something else)
So many databases!! there are lots of databases around. SQLite is super lightweight and simple to install. it's very widely used, you just never see it. and since we're using an ORM, it doesn't matter which you use – you can swap it out later! - I mean, https://en.wikipedia.org/wiki/Comparison_of_relational_database_management_systems - and these are just the relational ones
SQLAlchemy SQLAlchemy is the Python ORM we'll be using. Flask has a SQLAlchemy extension we can install using pip. pip install flask-sqlalchemy then in your Flask app… from flask import Flask from flask_sqlalchemy import SQLAlchemy app = Flask(__name__) ... db = SQLAlchemy(app) # and now db is your database!
Flask models
Remember MVC? a View presents the data from a Model to the user. x = 10 users = {...} name = 'bob' a Model is program state: variables, data structures, etc. if x == 10: for u in users: u.age += 1 elif name == 'bob': name = 'jill' a Controller is code that manipulates the Model.
test.db How SQLite works let's follow along with fl9_model.py for these next few slides. SQLite keeps the entire database in a single file on your hard drive. x = 10 users = {...} name = 'bob' test.db that's it. it's great. it's often used in embedded devices; for configuration files; or just as a binary file format. our Flask app needs to know what filename to use, but that's all the config we have to do. - I think Android provides sqlite DBs to all apps, that's what stores settings and stuff. - lots of embedded devices like routers etc. use sqlite for all their data.
notice each class has a primary key. Making a model once you've created your database object, you can make your model classes by extending its .Model class. any database columns are created in the class, outside the methods, which is unusual for Python. the constructor (__init__) is actually optional; by default it generates one for you, but it requires you to specify all columns by name. notice each class has a primary key. the unique constraint makes it impossible to insert two or more rows with the same value for that column.
Initializing a database when first spinning up your app, you may need to do some work to get the database into an empty, but consistent state. deleting a table is called dropping it. db.drop_all() deletes all tables (!!!). db.create_all() creates all the tables for the models you defined. db.session represents the current transaction. you add things to the session, then commit it to push all those changes to the DB at once.
Queries queries look like chains of method calls instead of like SQL statements… but you can look at the underlying SQL, too! filter_by() is used for looking for exact values. filter() is more general-purpose. first() and all() give you 1 or all results. order_by() does just that! you can look at the SQL by just printing out the query without using first/all.