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.
FB birthday data
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!
See an error in this post? Please submit a pull request on Github.