Skip to content

Commit

Permalink
Chapter 10: Caching of user avatar hashes (10d)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Jun 9, 2019
1 parent 168a9e8 commit 20ce2f9
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 6 deletions.
9 changes: 8 additions & 1 deletion app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class User(UserMixin, db.Model):
about_me = db.Column(db.Text())
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
avatar_hash = db.Column(db.String(32))

def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Expand All @@ -89,6 +90,8 @@ def __init__(self, **kwargs):
self.role = Role.query.filter_by(name='Administrator').first()
if self.role is None:
self.role = Role.query.filter_by(default=True).first()
if self.email is not None and self.avatar_hash is None:
self.avatar_hash = self.gravatar_hash()

@property
def password(self):
Expand Down Expand Up @@ -154,6 +157,7 @@ def change_email(self, token):
if self.query.filter_by(email=new_email).first() is not None:
return False
self.email = new_email
self.avatar_hash = self.gravatar_hash()
db.session.add(self)
return True

Expand All @@ -167,9 +171,12 @@ def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)

def gravatar_hash(self):
return hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()

def gravatar(self, size=100, default='identicon', rating='g'):
url = 'https://secure.gravatar.com/avatar'
hash = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
hash = self.avatar_hash or self.gravatar_hash()
return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(
url=url, hash=hash, size=size, default=default, rating=rating)

This comment has been minimized.

Copy link
@ezebunandu

ezebunandu Aug 25, 2020

Is there any security implication to using f-strings for this return statement, as in: return f'{url}/{hash}?s={size}&d={default}&r={rating}'

It seems to be less verbose, but I was wondering if there is a particular reason why you choose to use format strings instead, beyond maybe compatibility with older versions of Python. Thanks

This comment has been minimized.

Copy link
@miguelgrinberg

miguelgrinberg Aug 25, 2020

Author Owner

It's totally fine. I did not use f-strings because my target for this code was Python 3.5+. If I did not need to target 3.5 I would also use f-strings here.

This comment has been minimized.

Copy link
@ezebunandu

ezebunandu Aug 25, 2020

Great. Thanks, Miguel!

I am obsessed with f-strings and try to use them as much as I can.


Expand Down
26 changes: 26 additions & 0 deletions migrations/versions/198b0eebcf9_caching_of_avatar_hashes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""caching of avatar hashes
Revision ID: 198b0eebcf9
Revises: d66f086b258
Create Date: 2014-02-04 09:10:02.245503
"""

# revision identifiers, used by Alembic.
revision = '198b0eebcf9'
down_revision = 'd66f086b258'

from alembic import op
import sqlalchemy as sa


def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('avatar_hash', sa.String(length=32), nullable=True))
### end Alembic commands ###


def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'avatar_hash')
### end Alembic commands ###
6 changes: 1 addition & 5 deletions tests/test_user_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,8 @@ def test_gravatar(self):
gravatar_256 = u.gravatar(size=256)
gravatar_pg = u.gravatar(rating='pg')
gravatar_retro = u.gravatar(default='retro')
with self.app.test_request_context('/', base_url='https://example.com'):
gravatar_ssl = u.gravatar()
self.assertTrue('http://www.gravatar.com/avatar/' +
self.assertTrue('https://secure.gravatar.com/avatar/' +
'd4c74594d841139328695756648b6bd6'in gravatar)
self.assertTrue('s=256' in gravatar_256)
self.assertTrue('r=pg' in gravatar_pg)
self.assertTrue('d=retro' in gravatar_retro)
self.assertTrue('https://secure.gravatar.com/avatar/' +
'd4c74594d841139328695756648b6bd6' in gravatar_ssl)

0 comments on commit 20ce2f9

Please sign in to comment.