2015-01-28 5 views
2

Предположим, у меня есть следующие 3 модели, где каждая из двух моделей имеет отношение M2M к третьей модели.Django M2M Filter QuerySet с точным набором 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 объект и FirstModelQuerySet. Мне нужно фильтровать QuerySet так, что в результате QuerySet содержит только FirstModel объекты, которые имеют те же M2M множество связей с ThirdModel в M2M отношениях SecondModel объекта, установленных с 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? Я пробовал следующее безрезультатно (после анализа необработанного запроса очевидно, почему это не сработает).

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()) 
     ) 
    ) 

ответ

0

Я не уверен, что самый вещий способ, но, вероятно, самый Djangonic способом будет использование custom Manager.

В Django 1.6 и до этого, вам необходимо настроить диспетчер и 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) 
+0

Только для clarificat когда вы говорите «ваш код здесь», в каком коде вы ссылаетесь? Код 'some_filtering_method' в моем вопросе или какой-то пользовательский код, который нужно определить? – Rico

Смежные вопросы