One of my favorite Flask extensions is Flask-SQLAlchemy. It makes working with a database a breeze. For some time I wanted to detach my birthday management from Facebook. So I started a simple Flask app. Work so far here.
I am almost sure you could use the FB API before to pull all your friends and birthdays. Not anymore :(
Back to the article subject: how do we get this data into a DB? Flask-SQLAlchemy to the rescue:
First I defined a simple model in model.py:
... class Birthday(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(120)) bday = db.Column(db.DateTime) notify = db.Column(db.Boolean) def __init__(self, name, bday, notify=False): self.name = name self.bday = bday self.notify = notify def __repr__(self): return '<Birthday %r %r %r>' % (self.name, self.bday, self.notify)
It's best to store dates as db.DateTime objects so we can easily query them (see further down).
If model.py is run as standalone script (not imported) it recreates the DB:
I use the bdays.py ics parsing code to populate the table with all birthdays. You can even strip out the names (which was useful to share printscreens here). I store all birthdays with the same year (calendar ics ranges May '17 - May '18), otherwise the date querying fails (next step):
# insert birthdays sorted for bd in sorted(get_birthdays('cal.ics'), key=lambda x: (x.bday.month, x.bday.day)): # no real names if TEST_MODE: name = get_random_name() else: name = bd.name # import all bdays with THIS_YEAR to make it easier to query later bday = bd.bday.replace(year=THIS_YEAR) bd_obj = Birthday(name, bday) db.session.add(bd_obj) db.session.commit()
The app is still very bare-bones. It has an index/home route, to get the birthdays of the next 14 days, and a route to get birthdays for each month. See app.py. Here's why you want to work with datetime objects, it makes querying dates easier:
Upcoming n days:
@app.route('/') ... start = datetime.now() end = start + timedelta(days=UPCOMING_DAYS) bdays = Birthday.query.filter(Birthday.bday <= end).filter(Birthday.bday >= start)
How to get all birthdays of month n (see SO). The SQLAlchemy query is the same:
@app.route('/<int:month>') ... _, num_days = calendar.monthrange(THIS_YEAR, month) start = date(THIS_YEAR, month, 1) end = date(THIS_YEAR, month, num_days) bdays = Birthday.query.filter(Birthday.bday <= end).filter(Birthday.bday >= start)
You can use the calendar module to get the month name for a month int:
month_name = calendar.month_name[month]
This is it for starters. In part 2 I will make the app more functional:
Implement notifications: email me one day before a birthday.
Allow setting of notify == True for individual friends so I only get the notifications I want.
Full CRUD: add/update/delete friends and/or re-import new ics download.
Second (relational) model for tracking:
add the notifications sent to this table
have a "done" flag to update when I sent Happy Birthday wishes to a particular friend.
This is a nice extension to use Flask-SQLAlchemy's db.ForeignKey.
Flask-SQLAlchemy docs is a great start.
See our code challenge 15 review for more example apps using Flask-SQLAlchemy.
For examples of standard SQLAlchemy (outside Flask), our code challenge 17 review has some examples.
To learn SQLAlchemy start with the Object Relational Tutorial.
Keep Calm and Code in Python!