views:

52

answers:

3

Hello all,

I am working on a membership application. I would like to make a membership reminder. (member during a period of time which is not member for another period of time).

Currently, I am using set for making this calculation. See the code below.

class Member(models.Model):
     ...

class Membership(models.Model):
    member = models.ForeignKey(Member, verbose_name=_("Member"))
    start_date = models.DateField(_("Start date"))
    end_date = models.DateField(_("End date"))

x = Member.objects.filter(Q(membership__start_date__lte=dt1) & Q(membership__end_date__gte=dt1))
y = Member.objects.filter(Q(membership__start_date__lte=dt2) & Q(membership__end_date__gte=dt2))
result = set(x) - set(y)

I would like to know of I can do it only by using the django ORM (filter, exclude, annotate, distinct ...)?

Thanks in advance for your help

UPDATE

In fact, my model is a bit more complex. I also have newspaper foreign key.

class Member(models.Model):
     ...

class Newspaper(models.Model):
     ...

class Membership(models.Model):
    member = models.ForeignKey(Member, verbose_name=_("Member"))
    start_date = models.DateField(_("Start date"))
    end_date = models.DateField(_("End date"))
    newspaper = models.ForeignKey(Newspaper)

I want to have the reminder for a given newspaper. In this case, the working query is

sin = models.Membership.objects.filter(start_date__lte=dt1,
                                               end_date__gte=dt1,
                                               newspaper__id=2)

sout = models.Membership.objects.filter(start_date__lte=dt2,
                                          end_date__gte=dt2,
                                          newspaper__id=2)
result = models.Member.objects.filter(membership__in=sin).exclude(membership__in=sout)

I think that this a more verbose version of the answer given Ghislain Leveque which is also working well for me.

Thanks to S.Lott and KillianDS for very valuable answers and sorry for not so clear question :)

+6  A: 

Isn't it simply negating the second expression and putting it in the same filter? So you have something like !(a&b), which equals to (!a)|(!b), in this case:

result = Member.objects.filter(membership__start_date__lte=dt1, membership__end_date__gte=dt1, ~Q(membership__start_date__lte=dt2) | ~Q(membership__end_date__gte=dt2))

note by the way that for simple anding and basic lookups you need no Q objects, like I showed with the first two lookup parameters. Anding happens just by passing multiple arguments, Q objects are needed for negating and OR'ing lookups.

KillianDS
It seems to work fine. Thank you for opening my eyes :)
luc
+1  A: 

You should try :

result = Member.objects.\
    filter(
        membership__start_date__lte = dt1,
        membership__end_date__gte=dt1).\
    exclude(
        pk__in = \
            Member.objects.filter(
                membership__start_date__lte = dt2,
                membership__end_date__gte = dt2).\
    values_list('pk')
Ghislain Leveque
+3  A: 

A relational database table is a set -- by definition. Set - is where not exists in SQL, which is exclude in Django's ORM.

It seems (without testing) that you're doing this.

result = Member.objects.filter(
    Q(membership__start_date__lte=dt1) & Q(membership__end_date__gte=dt1)
).exclude(
    Q(membership__start_date__lte=dt2) & Q(membership__end_date__gte=dt2)
)
S.Lott