Sharing a models.py file
Flask API and apscheduler scripts that use one models.py file
The purpose of this application is to experiment with a few things:
1. Using apscheduler
2. Using two scripts that share the same model.py file
Other features in this repo:
1. Builds flask API
2. SQLite database
Github Repository: https://github.com/costa-rica/weatherApp02
This article is the result of documenting important details I intend to leverage for a more complicated app that will combine recurring weather as well as other user data. That future application will probably use flask Blueprints. I’m not sure it will scale well but if it does, I’ll try to follow up this post.
Using Apscheduler
apscheduler is a python library that runs reoccurring code. I know it can do more stuff, but I focused on the ‘cron’ trigger. The trigger is the direction for how you want to scheduler to set off.
For example, the cron trigger in this application is set to run on the hour every hour. It runs 1am, 2am, 3am, etc. Also, I could have set the cron trigger to run Every Tuesday at 3pm.
The other trigger that is common is ‘interval’. An example of this trigger is, say, you just want it to run every 15 minutes or 15 seconds.
You might be asking why I didn’t just use an interval to run your job since I just want to run it every hour?
If I used the interval trigger for the job I would have to start the app exactly on the hour. Using the cron trigger the apscheduler knows to execute on the next hour, i.e. when the minutes in a clock are all at zero, regardless when I start the application.
The code for using this library can be simple. Here is a very simple example:
from apscheduler.schedulers.background import BackgroundScheduler
from datetime import datetime
def scheduler_funct():
scheduler = BackgroundScheduler()
job_call_weather = scheduler.add_job(call_weather, ‘cron’, hour = ‘*’, minute = ‘*’, second=’*/15')
scheduler.start() while True:
passdef call_weather():
print(f’@Calling weather {datetime.now()}’)if __name__ == ‘__main__’:
scheduler_funct()
The code above will print to the terminal “@Calling weather {time}”. This is basically what I am using for this application, which I’ve shared the github repo to at the top.
Sharing models.py file with two independent apps/scripts
This might be a little hacky, but it works. In fact, the stackoverflow post I used to help me with the model sharing got a negative point before I one up’ed it. I welcome anyone’s explanation of what’s wrong here. I will try to use this in a larger app using flask Blueprints… we’ll see how it goes.
The keys here are:
1. Build db in models.py
2. import sess object to each script file as well as the models you’re using
Since I have three tables/models at the top of my non-models.py files I import
from models import sess, Users, Locations, Weather
where sess is my Session() object. When I use flask-sqlahchemy I’m used to importing “db” which is:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
However, when I’m just using sqlalchemy I use:
from sqlalchemy.orm import sessionmaker, Session, relationship
sess = Session()
Where I was doing “db.session.query…”, “db.session.add…”, etc. now I’m doing “sess.query”, “sess.add”, etc.,
Models.py differences
The models.py file has a few notable differences compared to when using the flask-sqlalchemy version but I think the changes a pretty transparent and don’t require a lot of steps to see the analogous code.
The imports are a lot but for the most part they seem to make sense.
from sqlalchemy import create_engine, inspect
from sqlalchemy import Column, Integer, String, Text, Float, DateTime, ForeignKey
from sqlalchemy.ext.declarative import declarative_basefrom sqlalchemy.orm import sessionmaker, Session, relationship
Building the tables/models are similar except there is no “db” object. For example instead of using db.Model as the parent class I am using “Base” which is the declarative_base() callable. In my version this is how I set up a model class:
Base = declarative_base()
class Users(Base):
__tablename__ = ‘users’
id = Column(Integer, primary_key = True)
username = Column(Text)
Column is now its own class from the sqlachemy library whereas before it was an attribute of “db”.
Creating the database can be done in a few places. I have chosen to build it in the models.py file so I don’t have to import “Base” or “engine” to another file. This will run before any other app runs as long as at least one model is imported to that file.
I think the logic is pretty straightforward, since you’ll be importing a model to any file that uses it, Python will execute what is in the models.py file before going on to do anything else with the database data.
Placing this bit of code in the models.py file makes the most sense.
if ‘users’ in inspect(engine).get_table_names():
print(‘db already exists’)
else:
Base.metadata.create_all(engine)
print(‘NEW db created.’
Previously, I had this code in other places like the route of the api and the scheduler.py file. That works fine so if for some reason you want to do that, you’ll just need to import the “Base” and “engine” objects.