Django排除子查询中的多个字段

时间:2016-06-24 13:15:22

标签: python django django-orm

给出一些像这样的代码:

# coding: utf-8
import datetime

from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site


class Premium(models.Model):
    """Access to Premium Features™®."""
    end = models.DateField()
    user = models.ForeignKey(User)
    site = models.ForeignKey(Site)


def get_ending_premiums():
    """Get a queryset of all Premiums for which a user has none following."""
    tomorrow = datetime.date.today() + datetime.timedelta(days=1)
    future_premiums = Premium.objects.filter(end__gt=tomorrow).values('user', 'site')
    return Premium.objects.filter(end=tomorrow).exclude(

        # Would love if something like this actually worked...
        user_and_site__in=future_premiums,
    )

如何完成get_ending_premiums()?其中一个关键的事情是,我只想在以后结束的另一个,基于每个网站的情况下才需要付费。因此,如果用户在groceries.com上有另一个Premium,明天即将结束的那个不会被退回,但如果他们没有在officesupplies.com上有另一个Premium,那么会 em>返回。

(注意在它之前没有实际工作的评论行......这是我需要完成的部分。)

我可以在ORM之外找出如何做到这一点,但我真的更喜欢ORM解决方案,因为我们计划在几个月内切换数据库供应商,所以我试图避免使用原始SQL可能的。

这是对我想要获得的行为的测试:

class PremiumTest(TestCase):

    def test_gets_ending_premiums(self):
        today = date(2020, 6, 5)
        tomorrow = today + timedelta(days=1)
        next_year = today + timedelta(days=366)
        groceries = Site.objects.create(domain='groceries.com')
        catvids = Site.objects.create(domain='catvids.com')
        dave = User.objects.create_user('dave')
        sally = User.objects.create_user('sally')
        Premium.objects.create(user=dave, site=groceries, end=tomorrow)
        Premium.objects.create(user=dave, site=groceries, end=next_year)
        Premium.objects.create(user=dave, site=catvids, end=tomorrow)
        Premium.objects.create(user=sally, site=groceries, end=tomorrow)
        Premium.objects.create(user=sally, site=catvids, end=tomorrow)
        Premium.objects.create(user=sally, site=catvids, end=next_year)

        ending_premiums = get_ending_premiums(today)
        ending = set((p.user, p.site) for p in ending_premiums)

        self.assertNotIn((dave, groceries), ending)
        self.assertIn((dave, catvids), ending)
        self.assertIn((sally, groceries), ending)
        self.assertNotIn((sally, catvids), ending)
        self.assertEqual(2, len(ending_premiums))

1 个答案:

答案 0 :(得分:1)

我已经提出了这个......它有一些原始的SQL,但它仍然返回一个带有正常QuerySet方法的QuerySet(尽管它使用了明显弃用的def get_ending_premiums(day=None): """Get a queryset of Premiums for which a user has none following.""" if day is None: day = date.today() tomorrow = day + timedelta(days=1) ending_premiums = Premium.objects.filter( end=tomorrow, ).extra( where=['NOT EXISTS (SELECT NULL FROM premium_premium child where premium_premium.site_id = site_id AND premium_premium.user_id = user_id AND end > %s )'], params=[tomorrow], ) return ending_premiums 方法)

Month(Now()) = '1'

仍然想知道是否有更好的方法......