From f2fb59966436ea689c836df2823cebd7aa499a1a Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Tue, 4 Aug 2020 11:36:53 +0200 Subject: [PATCH] Optimize Alias.get_contacts() to retrieve the latest reply for each contact in a single query --- .../dashboard/alias_contact_manager.html | 5 ++-- app/models.py | 28 +++++++++++++++---- shell.py | 3 ++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/dashboard/templates/dashboard/alias_contact_manager.html b/app/dashboard/templates/dashboard/alias_contact_manager.html index d3c3e5bc..e9c1bd21 100644 --- a/app/dashboard/templates/dashboard/alias_contact_manager.html +++ b/app/dashboard/templates/dashboard/alias_contact_manager.html @@ -89,9 +89,8 @@
Created {{ contact.created_at | dt }}
- {% if contact.last_reply() %} - {% set email_log = contact.last_reply() %} - Last email sent {{ email_log.created_at | dt }} + {% if contact.latest_reply %} + Last email sent {{ contact.latest_reply | dt }} {% endif %}
diff --git a/app/models.py b/app/models.py index 86d3143d..a4dc3f59 100644 --- a/app/models.py +++ b/app/models.py @@ -2,20 +2,19 @@ import enum import random import uuid from email.utils import formataddr -from typing import List, Tuple +from typing import List, Tuple, Optional import arrow import bcrypt +from arrow import Arrow from flask import url_for from flask_login import UserMixin -from sqlalchemy import text, desc, CheckConstraint +from sqlalchemy import text, desc, CheckConstraint, and_, func from sqlalchemy.exc import IntegrityError -from sqlalchemy.orm import joinedload from sqlalchemy_utils import ArrowType from app import s3 from app.config import ( - EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN, URL, AVATAR_URL_EXPIRATION, @@ -914,13 +913,27 @@ class Alias(db.Model, ModelMixin): return self.user.email def get_contacts(self, page=0): - contacts = ( - Contact.filter_by(alias_id=self.id) + latest_reply = func.max(EmailLog.created_at) + q = ( + db.session.query(Contact, latest_reply) + .join( + EmailLog, + and_(EmailLog.contact_id == Contact.id, EmailLog.is_reply), + isouter=True, + ) + .filter(Contact.alias_id == self.id) + .group_by(Contact.id) .order_by(Contact.created_at.desc()) .limit(PAGE_LIMIT) .offset(page * PAGE_LIMIT) .all() ) + + contacts = [] + for contact, l in q: + contact.latest_reply = l + contacts.append(contact) + return contacts def __repr__(self): @@ -1050,6 +1063,9 @@ class Contact(db.Model, ModelMixin): alias = db.relationship(Alias) user = db.relationship(User) + # the latest reply sent to this contact + latest_reply: Optional[Arrow] = None + @property def email(self): return self.website_email diff --git a/shell.py b/shell.py index bbed14b7..5072fbb5 100644 --- a/shell.py +++ b/shell.py @@ -8,6 +8,9 @@ from app.models import * from server import create_app from time import sleep +from sqlalchemy import * +from sqlalchemy.orm import * + def create_db(): if not database_exists(DB_URI):