具有精确M2M集的Django M2M过滤器查询集

时间:2015-01-28 23:28:37

标签: django many-to-many

假设我有以下3个模型,其中2个模型中的每个模型与第3个模型都有M2M关系。

class FirstModel(models.Model):
    third_model = models.ManyToMany('ThirdModel')


class SecondModel(models.Model):
    third_model = models.ManyToMany('ThirdModel')


class ThirdModel(models.Model):
    pass

现在,假设我有一个特定的SecondModel对象和一个FirstModel QuerySet。我需要对QuerySet进行过滤,以便生成的QuerySet仅包含与FirstModel关系设置为M2M的{​​{1}}个ThirdModel对象SecondModel 1}}对象的M2M关系设置为ThirdModel

def some_filtering_method(first_model_qs, second_model):
    third_models_set = second_model.third_model_set.all()
    first_model_ids = list()
    for third_model in third_models_set:
        first_model_ids.append(
            [first_model.pk
             for first_model in third_model.first_model_set.all()])
    intersection_of_first_model_ids = get_intersection(first_model_ids)
    return first_model_qs.filter(pk__in=intersection_of_first_model_ids)

在Django中有更多Pythonic方式吗?我尝试了以下但没有成功(在查看原始查询之后,显然它为什么不起作用)。

import operator

from django.db.models import Q


def some_filtering_method(first_model_qs, second_model):
    return first_model_qs.filter(
        reduce(
            operator.and_, 
            (Q(third_model_set__contains=x) 
             for x in second_model.third_model_set.all())
        )
    )

1 个答案:

答案 0 :(得分:0)

我不确定最恐怖的方式,但最可能是 Djangonic 的方式是使用custom Manager

在Django 1.6和之前的版本中,您需要单独设置Manager和QuerySet:

class SharedM2MSetQuerySet(models.QuerySet):
    def shared(self):
        pass
        # your code here

class SharedM2MSetManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def shared(self,other):
        return self.get_queryset().shared(other)

    class FirstModel(models.Model):
        third_model = models.ManyToMany('ThirdModel')
        manager = SharedM2MSetQuerySet.as_manager()

如果您使用的是Django 1.7,可以使用QuerySet as a Manager快捷方式,如下所示:

class SharedM2MSetQuerySet(models.Manager):
    def shared(self,other):
        pass
        # your code here

class FirstModel(models.Model):
    third_model = models.ManyToMany('ThirdModel')
    manager = SharedM2MSetQuerySet.as_manager()

class SecondModel(models.Model):
    third_model = models.ManyToMany('ThirdModel')

class ThirdModel(models.Model):
    pass

在这两种情况下,这都允许您执行以下操作:

my_obj = SecondModel.objects.first()
for i in FirstModel.objects.shared(my_obj):
    do_the_thing(i)