- REQUIRES PYTHON 3.11 FOR THE
| Noneinsidemobile2: Mapped[int | None] = mapped_column(String(30))in SQLAlchemy Models. - Use
virtualenvto manage python versions. macOS for example comes with 3.9 and one can install version 3.11 in a parallel location by downloading installer from python.org
git clone https://github.com/drguptavivek/backend.git
cd backend
python3 -m venv vnev
# ALTERNATIVE
pip install --user virtualenv
virtualenv venv
virtualenv -p /usr/bin/python3 venv
# Python 3.9.6
# Install Python 3.11 from https://www.python.org
virtualenv -p /usr/local/bin/python3 venv
virtualenv -p /Library/Frameworks/Python.framework/Versions/3.11/bin/python3.11 venvsource venv/bin/activate
pip install -r requirements.txt
rm -r migrations
flask --app backend empty-db
flask db init
flask db migrate
flask db upgrade
flask --app backend seed-db - The 'main' branch contains all the main code -
git clone https://github.com/drguptavivek/backend.git; git checkout main - Create branches for development locally
git branch vivek_macbook; git checkout vivek_macbook - All work done on the various machines will be COMMITTED locally
git add; git commit. - Once work is completed -PUSH from local branch to the GitHub remote / upstream branch
git push -u origin vivek_macbook - Got to GitHub website and create a PULL REQUEST (merge request): e.g.
- Merge the branch on GitHub website with
mainbranch - Checkout the
mainon local machinegit remote show origin ; git checkout main- Now the
mainon local desktop/laptop would be behind the server main
- Now the
- Pull the changes from
mainon server tomainon local -git pull
git clone https://github.com/drguptavivek/backend.git
git checkout main
git remote show origin
git branch -a
# Create branch for local work
git branch vivek_macbook
git checkout vivek_macbook
git add
git commit -m "About to Git push a local branch upstream to a remote GitHub repo."
# Push local branch code to remote
git push -u origin vivek_macbook
# remote: Create a pull request for 'vivek_macbook' on GitHub by visiting:
# remote: https://github.com/drguptavivek/backend/pull/new/vivek_macbook
# merge on GitHub; Delete remote vivek_macbook on GitHub
# Back on Laptop
git remote show origin
git checkout main
git pull
git branch --d vivek_macbook
git push origin --delete vivek_macbook
git branch vivek_macbook
git checkout vivek_macbook
git branch -a
git add
git commit -m "About to Git push a local branch upstream to a remote GitHub repo."
# Push local branch code to remote
git push -u origin vivek_macbook
# merge on GitHub; Delete remote vivek_macbook on GitHub
# Back on Desktop
git remote show origin
git checkout main
git pull
git branch --d vivek_desktop # Detach / delete preexisting local branch vivek_desktop
git push origin --delete vivek_desktop # Detach / delete preexisting REMOTE branch vivek_desktop
git branch vivek_desktop
git checkout vivek_desktop
git branch -a
git add
git commit -m "About to Git push a local branch vivek_desktop upstream to a remote GitHub repo at vivek_desktop."
# Push local branch code to remote
git push -u origin vivek_desktop
# merge vivek_desktop with main on GitHub; Delete remote vivek_desktop on GitHub
config.py: various configuration sets that can be called when instantiating the Flask app- Each configuration set includes parameters. Non-secret configurations can be declared. Secret configs can be loaded from .env
.env, .env.production: various secrets that can be injected in each configuration inside config.py
- The backend directory contains all application logic
- init.py includes the factory pattern for creation of Flask app
config_class=DevConfigmeans it will load theDevConfigconfiguration fromconfig.pywhich loads secrets from.env
- Initialize the SQLAlchemy db objects and other extensions such as Migrate, Marshmallow etc. in
extensions.py:db = SQLAlchemy() - Import extension objects in
__init__.pyinside thecreate_appfactory, - Inject the initialized Extension objects in the app inside
app.app_context - Models:
- Create model in
backend/models/xxx_model.py.- Import the
dbobject frommyApp/db.py - Create the Model classes using the SQLAlchemy 2.0 Declarative Syntax using Type Hints
- Import the
- Import the declared model classes in
models_import.py:from backend.models.user_model import Department,.... - The models_import.py has already been made available inside the
app.app_contextin__init__.py. This allows Flask-Migrate to get all models and run migrations on them
- Create model in
- Seeding: The db_initializer folder contains an
db_initializer.pydb.engine.url.database- is used to get the current database name- Functions such as
create_department(), create_faculty_cadre(), create_user()have been created. Each function- Truncates the tables affected by that class - Table name is manually being set inside function. Foreign Key checks arr disabled and re-enabled
- Creates the objects of Model . Example - Department model class
department = Department(...) - Adds data for various named properties of the Model class
- Leverages relationships to add child objets using the related child Model properties: eg
department = Department(... departmentUnits=[Unit(...), Unit(...),Unit(...)])
clickis used to add commandline functions to EMPTY-DB and SEED-DB.@click.command('seed-db') def seed_db_command():ClickFunctions once declared indb_initializer.pyare registered with the main init.py inside application context -app.cli.add_command(seed_db_command)
https://flask-migrate.readthedocs.io/en/latest/
- Import and instantiate Flask Migrate in
extensions.py - Inject the created db and app objects in Migrate db object
migrate = Migrate(app, db)inside thecreate_appfactory inapp.app_context
- use new Python Type Hinted Mapped_column syntax
https://docs.sqlalchemy.org/en/20/orm/basic_relationships.html#one-to-many
Department : One side Unit: Many Side
- Add Foreign Key on the Many Side: Unit class
department_id: Optional[Mapped[str]] = mapped_column(ForeignKey('departments.id'), index=True) - Add Relationship on the many Side: Unit class - used singular since a unit can have one department
department: Mapped['Department'] = relationship(back_populates='units')3 - Add Relationship on the One Side: Department class - used plural since a department can have many units
units: Mapped[list['Unit']] = relationship(back_populates='department')
Explanation
- Each Unit instance will have a department_id.
- Getting a Unit instance will get the related Department instance using the called 'units' relationship
- Getting a Department will get all the related multiple Unit instances using the called 'department' relationship
- select loader is a lazy loader and is default. The DB query for related object is delayed till that relationship attribute is accessed for the first time
- joined loader is an eager loader - it accesses all related objects at teh same time as parent is called
- useful if you know you will be accessing related objects
- Other loaders:
raise, raise_on_sql, selectin, write_only, immediate, noload - Default loader for a relationship can be changed using the
lazy=argument - For the User Class
- joined loader makes sense since it would be good to get the name of the designation as soon as a user is accessed
designation: Mapped[Designation] = relationship(back_populates='users', lazy='joined') - HOWEVER, joined loader causes problems with SELECT statements necessitating the need of UNIQUE() otherwise multiple related rows are fetched
- joined loader makes sense since it would be good to get the name of the designation as soon as a user is accessed
- For the Designation Class, select loader makes more sense as we may not want to typically get all users of designation
- DETACH children is parent deleted; Do Not BLOCK Parent deletion if children are present
- The FKey is ALLOWED NULLS / Optional
- cascade = 'save-update, merge'
- is the DEFAULT behaviour
class Department(db.Model):
__tablename__ = "departments"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(str(30), unique=True, index=True)
units: Mapped[list['Unit']] = relationship(back_populates='department', cascade = 'save-update, merge', lazy='joined')
class Unit(db.Model):
__tablename__ = "units"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(str(30), unique=True, index=True)
department_id: Optional[Mapped[str]] = mapped_column(ForeignKey('departments.id'), index=True)
department: Mapped['Department'] = relationship(back_populates='units', lazy='joined')
- DELETE children is parent deleted; Block parent deletion if children are present
- The FKey is NOT NULL / NOT Optional
- cascade = 'all, delete-orphan'
class Department(db.Model):
__tablename__ = "departments"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(str(30), unique=True, index=True)
units: Mapped[list['Unit']] = relationship(back_populates='department', cascade = 'all, delete-orphan', lazy='joined')
class Unit(db.Model):
__tablename__ = "units"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(str(30), unique=True, index=True)
department_id: Mapped[str] = mapped_column(ForeignKey('departments.id'), index=True)
department: Mapped['Department'] = relationship(back_populates='units', lazy='joined')
- DO not Delete the children if a prent gets deleted. This is Optional to allow setting to NULL in case a department gets deleted
Declare schemas in 'schemas' folder
- Create individual Blueprint specific folders inside views oor apis directory. e.g. admin
- Create init.py inside the blueprint specific folder
- Create a Blueprint_bp.py file inside the blueprint specific folder
- Instantiate a blueprint object
- Add Blueprint Views
- Register the blueprint with its URL prefix in myApp/init.py