过滤Django中相关的最新条目和预取

时间:2017-04-12 08:49:56

标签: python django postgresql django-queryset

我一直在尝试优化我在Django中的查询以减少我的postgres数据库命中,所以这里是简化这个问题的关注模型

型号:

class SentEmail(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    sent_date = models.DateTimeField(default=timezone.now)    
    sender = models.EmailField()


class AppUser(models.Model):
    user= models.OneToOneField(User, primary_key=True)
    is_active= models.BooleanField(default= False)
    expiration_date = models.DateTimeField(default=make_default_expiration_date) #note : 90 days after the creation

User是标准的Django auth用户,其中包含用户名,密码和其他内容。我想要做的是检索活跃用户的AppUser各种数据以及与他们相关的最后一个SentEmail。现在,我有这个。

查看:

active_users = Language_new.objects.filter(expiration_date__gt=timezone.now(),
                                           is_active=True)

如果我想要包含上次发送的电子邮件,我会做这样的事情

active_users = Language_new.objects.filter(expiration_date__gt=timezone.now(),
                                           is_active=True).prefetch_related(
                Prefetch(
                    "sentemail",
                    queryset=SentEmail.objects.filter().latest('sent_date'),
                    to_attr="last_sentemail"
                ))

问题:

Django是否足够聪明,可以在这里建立关系:queryset=SentEmail.objects.filter().latest('sent_date')?如果没有,我如何在filter()

中引用每个用户

另外,因为可能没有"最新的"每个sentemail AppUser,如何在此类查询中处理DoesNotExist

代码执行:

执行此代码会得到一个AttributeError

AttributeError: 'SentEmail' object has no attribute '_iterable_class'

指着这一行:to_attr="last_sentemail"。我不知道为什么。

丑陋的解决方案:

请注意,我的工作解决方案如下所示:

active_users = Language_new.objects.filter(expiration_date__gt=timezone.now(),
                                               is_active=True)

active_users = list(active_users)
for user in active_users:
     last_sent_mail = SentEmail.objects.filter(user=active_user.user)
     if last_sent_mail.exists():
         setattr(user, 'last_sent_mail_date', last_sent_mail.latest('sent_date').sent_date)
     else:
         setattr(user, 'last_sent_mail_date', 'None')

它正在工作,但我认为我们都同意它并不是最好的。

注意:我在这里直接检索了上一个sentemail的日期,但是完整的sentemail或其日期都适用于我。

感谢。

1 个答案:

答案 0 :(得分:1)

尝试:

class SentEmail(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name='sent_mails'
    )
    sent_date = models.DateTimeField(default=timezone.now)    
    sender = models.EmailField()

class AppUser(models.Model):
    user= models.OneToOneField(User, primary_key=True)
    is_active= models.BooleanField(default= False)
    expiration_date = models.DateTimeField(
         default=make_default_expiration_date
    ) #note : 90 days after the creation

    @property
    def last_sent_mail(self):  # or last_sent_mail_date
        sent_mails = self.user.sent_mails.order_by('sent_date')
        return sent_mails[0] if sent_mails.exists() else None

检索上次发送的电子邮件:

last_mail = user.last_sent_mail
if last_mail:
    print last_mail.sent_date