【问题标题】:Filtering latest entry and prefetch related in Django过滤Django中相关的最新条目和预取
【发布时间】:2017-04-12 08:49:55
【问题描述】:

我一直在尝试优化我在 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 身份验证用户,具有用户名、密码和其他内容。我想要做的是为活跃用户检索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() 中的每个用户?

另外,由于每个AppUser 可能没有“最新”sentemail,我该如何处理这种查询中的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 或其日期都对我有用。

谢谢。

【问题讨论】:

    标签: python django postgresql django-queryset


    【解决方案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
    

    【讨论】:

    • 谢谢,它有效。我仍然没有直接在模型上工作来帮助我构建请求的习惯。只是一个错字,应该是sent_mails = self.user.sent_mails.order_by('sent_date') 而不是sent_mails = self.sent_mails.order_by('sent_date')
    • 很高兴它有帮助。我已按照您的建议更改了代码。
    猜你喜欢
    • 2015-05-02
    • 2013-05-06
    • 2011-07-25
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2020-08-15
    • 2021-08-02
    相关资源
    最近更新 更多