---
# System prepended metadata

title: (Archive) Using SQLAlchemy with Flask

---

<style>
    .present {
        text-align: left;
    }
</style>

# (Archive) Using SQLAlchemy with Flask
## Week 18 Day 3

---

## Lecture Videos 1 (18min)
Watch:
- More About Classes (15:55)

---

### Instance variables
- Specific to each instance of the class—changing on one instance won't affect another
- Defined in the constructor and attached to `self`
- You can access on an instance, but not on the class as a whole


```python=
class Widget:
    # constructor
    def __init__(self, color):
        # instance variables
        self.color = color


my_widget = Widget("blue")
print(my_widget.color)  # "blue"
print(Widget.color)     # error 
```

---

### Class variables
- Shared across instances and on the class itself
- Each instance points to the same class variables, so updating the value on the class will change for all instances (unless their value was already changed)
- Updating on one instance will not change other instances


```python=
class Widget:
    price = "$5"
    def __init__(self, color):
        # instance variables
        self.color = color


my_widget = Widget("blue")
second_widget = Widget("chartreuse")
print(my_widget.price)  # "$5"
print(Widget.price)     # "$5"
my_widget.price = "$100"
print(second_widget.price) # "$5"
print(Widget.price)        # "$5"
Widget.price = "$50"
print(second_widget.price) # "$50"
print(my_widget.price)     # "$100"
```



---


### Instance methods
So far, we've written instance methods on classes—that is the default type of method on a class.
- `self` is passed in implicitly as the first parameter—this refers to the instance the method was invoked on

```python=
# inside class
def greet_widget(self):
    return f"hello, {self.color} widget"

my_widget = Widget("blue")
print(my_widget.greet_widget())  # "hello blue widget"
```

---

### Class methods
We can use the `@classmethod` decorator to write class methods. 

The first argument will refer to the class itself (conventionally called `cls`), rather than an individual instance.

```python
# inside class
@classmethod
def widget_factory(cls, colors):
    widgets = [cls(color) for color in colors]
    print([widget.greet_widget() for widget in widgets])
    return widgets


print(Widget.widget_factory(["red", 
"yellow", "beige"]))
```

---

### Static methods
Static methods don't take implicit arguments—they can't access the class or any instance of it.
```python=
@staticmethod
def something_about_widgets():
    return "widgets are neat"
```

---

## Lecture videos 2 (8 min)
Watch:
- FlaskSQLAlchemy Set-Up Best Practices (6:15)

---

### Steps for setting up Flask-SQLAlchemy

1. Create your database & user in psql.
```sql
create user widget_user with password 'password';
create database widget_database with owner 'widget_user';
```


2. Add a DATABASE_URL to your `.env`
```bash=
DATABASE_URL=postgresql://widget_user:password@localhost/widget_database
```

3. Install psycopg2, sqlalchemy, and flask-sqlalchemy
```bash
pipenv install psycopg2-binary sqlalchemy flask-sqlalchemy
```

---

### Steps for setting up Flask-SQLAlchemy

4. Modify your config class.
    a. Add a class variable called `SQLALCHEMY_DATABASE_URI`, and set the value to the `DATABASE_URL` from your environment
    b. Add a class variable called `SQLALCHEMY_TRACK_MODIFICATIONS` and set the value 

```python=
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL")
SQLALCHEMY_TRACK_MODIFICATIONS = False
```

---

### Steps for setting up Flask-SQLAlchemy


5. Create a models.py file, and instantiate a `SQLAlchemy` object from `flask_sqlalchemy`.

```python=
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
```

---

### Steps for setting up Flask-SQLAlchemy

6. Import the `db` instance into the file where you are defining your app.
```python=
from app.models import db
# much additional code...
db.init_app(app)
```


---

## Lecture Videos 3 (32 min)
Watch:
- FlaskSQLAlchemy: Creating Mapping Files (15:00)
- FlaskSQLAlchemy: Interacting with Data (13:10)

---


### Creating Models

Models inherit from the `db.Model` class, which comes from our `SQLAlchemy` instance.

If you want to specify a tablename, use the `__tablename__` property.

```python=
db = SQLALchemy()

class Widget(db.Model):
    __tablename__ = "widgets"
```

---

### Columns on models

Columns are added to the model as class variables—instances of `db.Column` from our `SQLAlchemy` instance.

Column types (`db.String`, `db.Integer`, etc.) also come from our SQLAlchemy instance.

```python=
class Widget(db.Model):
    __tablename__ = "widgets"
    id = db.Column(db.Integer, primary_key=True)
    color = db.Column(db.String(50), nullable=False)
```


---

### One-To-Many Relationships

Let's make a relationship between widgets and stores. Each widget belongs to one store, stores can have many widgets.

```python=
class Store(db.Model):
    __tablename__ = "stores"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), nullable=False)
```

---

### One-To-Many Relationships
First we need to add a foreign key to the widgets model that points to the store model. To make a column into a foreign key we use `db.ForeignKey(tablename.columnname)`.

```python=
class Widget(db.Model):
    __tablename__ = "widgets"
    id = db.Column(db.Integer, primary_key=True)
    color = db.Column(db.String(50), nullable=False)
    store_id = db.Column(db.Integer, db.ForeignKey("stores.id"))
```

---

### Adding the relationship property
Add a `db.relationship` to both of the models. Use the `back_populates` property to connect the two relationship attributes.

```python=
attribute_name = db.relationship("OtherModelName", back_populates="name_of_attribute_from_opposite_model")
```

```python=
# on the Widget model
store = db.relationship("Store", back_populates="widgets")
```

```python=
# on the Store model
widgets = db.relationship("Widget", back_populates="store")
```


---

### Many-to-Many Relationships

Many-to-many relationships are a lot like one-to-many relationships in terms of how they use the `db.relationship` property.

However, since neither of the two associated tables contains the foreign keys, we would need to build a separate association table to connect the models.

We would use a `db.Table` to do make an association table.

---

### Many-to-Many Relationships

The first argument to `db.Table` will be the table name. Each additional argument is a column. A typical association table only needs two columns—one for each of the two foreign keys.

It does not need its own id column (this is different from Sequelize).

```python=
widget_stores = db.Table(
    "widget_stores",
    db.Column(
        "widget_id", db.Integer, db.ForeignKey("widgets.id"), primary_key=True
    ),
    db.Column(
        "store_id", db.Integer, db.ForeignKey("stores.id"), primary_key=True
    )
)
```

---

### Many-to-Many Relationships

The only change we have to make the `db.relationship` attributes is to add a `secondary` argument. The value here is the table.

```python=
attribute_name = db.relationship(
    "OtherModelName",
    secondary=association_table_name,
    back_populates="name_of_attribute_from_opposite_model"
)
```


---

### Creating records

To make a new row in your database, first create a new instance of one of your model classes.

After you create your instance, add it to your session and commit it to the database.

```python=
new_widget = Widget(color="red")
db.session.add(new_widget)
db.session.commit()
```

---

### Querying with SQLAlchemy

To query the database, we use the `query` attribute on our `db.Model` classes.

Get all records:
```python=
widgets = Widget.query.all()
```

Get one record by primary key:
```python=
one_widget = Widget.query.get(id)
```

---

### Filtering on queries

To apply a filter, you can use the `.filter` method, passing in an operator on a column. Use `.all()` to get a list of all records that match the filter, or use `.first()` to get the first matching record.

```python=
# all widgets where color is blue
Widget.query.filter(Widget.color == "blue").all()
```

```python=
# all widgets where the color starts with "r"
Widget.query.filter(Widget.color.like("r%")).first()
```

[more available operators](https://docs.sqlalchemy.org/en/14/core/operators.html)

---

### Updating records
Once you have queried or created an instance of your model, you can update it by changing a value on that instance, and adding/commiting to the db.
```python=
widget = Widget.query.get(1)
widget.store_id = 1
db.session.add(widget)
db.session.commit()
```

---


### Deleting records

Deleting records uses the `delete` method on the `db.session` object.
```python=
widget = Widget.query.get(1)
db.session.delete(widget)
db.session.commit()
```