FastAPI - Beginner to Pro / Authentication & Authorization

Users Table Creation

πŸ—„οΈ Enhancing the Database β€” Introducing the Users Table

In this lesson, we are upgrading our database architecture.

Up to this point, we only had a Todos table inside a database named:

todos.db

However, this naming is confusing because:

  • We have a database called todos

  • Inside it, we also have a table called todos

That lacks clarity and does not scale well.

So we are going to rename the database.


πŸ“ Step 1 β€” Rename the Database

Inside database.py, where we configured SQLite, update the database name:

todos.db  β†’  todosapp.db

SQLALCHEMY_DATABASE_URL = "sqlite:///./todos.db" 

SQLALCHEMY_DATABASE_URL = "sqlite:///./todosapp.db" 

This makes the database represent the entire application, not just one table.

Now our database will contain:

  • users table

  • todos table

This is much cleaner and more scalable.


⚠️ Important Concept β€” SQLAlchemy Cannot Modify Tables

SQLAlchemy can:

βœ” Create tables

❌ Can not Modify existing tables

If we want to enhance or alter existing tables properly in production, we use Alembic (database migrations).

We will cover Alembic later in this course.


πŸ—οΈ Step 2 β€” Update models.py

Before recreating the database, we must define our new structure.

We will:

  • Add a new Users table

  • Enhance the Todos table

  • Add a Foreign Key relationship

πŸ‘€ Creating the Users Table

πŸ—‚οΈ models.py

from database import Base
from sqlalchemy import Column, Integer, String, Boolean

class Users(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True)
    username = Column(String, unique=True)
    first_name = Column(String)
    last_name = Column(String)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)
    role = Column(String)

πŸ” Understanding Hashed Passwords (Critical Backend Concept)

We never store plain-text passwords.

Instead:

  • User enters a password

  • We hash it

  • We store only the hash

Hashing is one-way and cannot be reversed.

During login:

  • Hash the entered password

  • Compare it with the stored hash

  • If they match β†’ login succeeds

βœ” Developers can’t see passwords

βœ” Leaked databases don’t expose passwords

βœ” We compare hashes, not plain text


πŸ“ Step 3 β€” Enhance the Todos Table

Now we update our existing Todos model.

πŸ—‚οΈ models.py

from sqlalchemy import Column, Integer, String, Boolean, ForeignKey

class Todos(Base):
    __tablename__ = 'todos'

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String)
    description = Column(String)
    priority = Column(Integer)
    complete = Column(Boolean, default=False)
    owner_id = Column(Integer, ForeignKey("users.id"))

πŸ”‘ What owner_id Means

owner_id = Column(Integer, ForeignKey("users.id"))

This means:

  • Every todo must reference a user.

  • owner_id stores the primary key of the user.

  • This creates a one-to-many relationship.

One user β†’ Many todos

One todo β†’ One user

Now when a user logs in:

  • We extract their id.

  • We fetch all todos where:

owner_id == user.id

That is how we securely isolate user data.


πŸš€ Step 4 β€” Recreate the Database

Now:

Run the application

terminal

Delete the old database todos.db and todosapp.db

del todos.db 
del todosapp.db
uvicorn main:app --reload

reload the browser

uvicorn main:app --reload
uvicorn main:app --reload

On startup:

  • SQLAlchemy reads the models.

  • Creates todosapp.db.

  • Creates both users and todos tables.

  • Applies the foreign key relationship.

⚠️ Important Reminder

Since we deleted the old database:

  • All previous todos are gone.

  • We will recreate data from scratch.

This is expected and correct.

We changed the schema.


πŸ—‚οΈ models.py

from database import Base
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey

class Users(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True, index=True)
    email = Column(String, unique=True)
    username = Column(String, unique=True)
    first_name = Column(String)
    last_name = Column(String)
    hashed_password = Column(String)
    is_active = Column(Boolean, default=True)
    role = Column(String)

class Todos(Base):
    __tablename__ = "todos"

    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, nullable=False)
    description = Column(String)
    priority = Column(Integer)
    complete = Column(Boolean, default=False)
    owner_id = Column(Integer, ForeignKey("users.id"))

No Changes in other files or folder structure