Writing custom functions with SQLite

SQLite is very easy to extend with custom functions written in Python, that are then callable from your SQL statements. By using the SqliteExtDatabase and the func() decorator, you can very easily define your own functions.

Here is an example function that generates a hashed version of a user-supplied password. We can also use this to implement login functionality for matching a user and password.

  1. from hashlib import sha1
  2. from random import random
  3. from playhouse.sqlite_ext import SqliteExtDatabase
  4.  
  5. db = SqliteExtDatabase('my-blog.db')
  6.  
  7. def get_hexdigest(salt, raw_password):
  8. data = salt + raw_password
  9. return sha1(data.encode('utf8')).hexdigest()
  10.  
  11. @db.func()
  12. def make_password(raw_password):
  13. salt = get_hexdigest(str(random()), str(random()))[:5]
  14. hsh = get_hexdigest(salt, raw_password)
  15. return '%s$%s' % (salt, hsh)
  16.  
  17. @db.func()
  18. def check_password(raw_password, enc_password):
  19. salt, hsh = enc_password.split('$', 1)
  20. return hsh == get_hexdigest(salt, raw_password)

Here is how you can use the function to add a new user, storing a hashed password:

  1. query = User.insert(
  2. username='charlie',
  3. password=fn.make_password('testing')).execute()

If we retrieve the user from the database, the password that’s stored is hashed and salted:

  1. >>> user = User.get(User.username == 'charlie')
  2. >>> print user.password
  3. b76fa$88be1adcde66a1ac16054bc17c8a297523170949

To implement login-type functionality, you could write something like this:

  1. def login(username, password):
  2. try:
  3. return (User
  4. .select()
  5. .where(
  6. (User.username == username) &
  7. (fn.check_password(password, User.password) == True))
  8. .get())
  9. except User.DoesNotExist:
  10. # Incorrect username and/or password.
  11. return False