Skip to content

Commit

Permalink
Optimize DB accesses by using an SQL JOIN when retrieving a user.
Browse files Browse the repository at this point in the history
`Datastore.find_user()` is used on every page load to verify the user in
the current session; until now, `find_user()` would trigger 2+ DB
accesses, since the `roles` relationship uses lazy loading by default.
This is useful to reduce the rows accessed when retrieving multiple
users from the DB.

However, in `find_user()` we always retrieve only one, so
it makes sense to retrieve all roles in one SQL operation instead of
always triggering 2 or more.

This would be unnecessary if the `roles` relationship was defined with
`lazy="joined"`. However, that would greatly increase the number of rows
accessed on all queries (even when querying all users), so I think it's
better to be selective and optimize only these accesses which happen on
every page load.
  • Loading branch information
nfvs authored and jirikuncar committed Jul 18, 2017
1 parent 4555e42 commit fd29734
Showing 1 changed file with 13 additions and 3 deletions.
16 changes: 13 additions & 3 deletions flask_security/datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,18 @@ def __init__(self, db, user_model, role_model):

def get_user(self, identifier):
from sqlalchemy import func as alchemyFn
from sqlalchemy.orm import joinedload
if self._is_numeric(identifier):
return self.user_model.query.get(identifier)
return self.user_model.query\
.options(joinedload('roles'))\
.get(identifier)
for attr in get_identity_attributes():
query = alchemyFn.lower(getattr(self.user_model, attr)) \
== alchemyFn.lower(identifier)
rv = self.user_model.query.filter(query).first()
rv = self.user_model.query\
.options(joinedload('roles'))\
.filter(query)\
.first()
if rv is not None:
return rv

Expand All @@ -251,7 +257,11 @@ def _is_numeric(self, value):
return True

def find_user(self, **kwargs):
return self.user_model.query.filter_by(**kwargs).first()
from sqlalchemy.orm import joinedload
return self.user_model.query\
.options(joinedload('roles'))\
.filter_by(**kwargs)\
.first()

def find_role(self, role):
return self.role_model.query.filter_by(name=role).first()
Expand Down

0 comments on commit fd29734

Please sign in to comment.