earn the White PyBites Ninja earn the Yellow PyBites Ninja earn the Orange PyBites Ninja right arrow earn more PyBites Ninja belts and certificates
The best way to learn to code in Python is to actually use the language.

Our platform offers effective Test Driven Learning which will be key to your progress.


Join thousands of Pythonistas and start coding!


Join us on our PyBites Platform
Click here to code!

Building a Simple Birthday App with Flask-SQLAlchemy

Posted by Bob on Thu 11 May 2017 in Flask • 3 min read

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 :(

Luckily I found a way to export them and parse them into a useful format - see bdays.py.

Starting Flask-SQLAlchemy

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:

    db.drop_all()
    db.create_all()
    

    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)
      

Resulting App

bday app upcoming

bday app for a particular month

You can use the calendar module to get the month name for a month int:

month_name = calendar.month_name[month]

See app.py.

TODO

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.

Resources


Keep Calm and Code in Python!

-- Bob


See an error in this post? Please submit a pull request on Github.