2012-05-25 3 views
0

편집 :어떻게 이러한 목록 필터를 db 쿼리로 변환 할 수 있습니까?

너무 많이 읽는 모든 사람을 방지하려면 : 대부분의 아마

class Item(models.Model): 

    tag_rep=generic.GenericRelation(TaggedItem) 

tag_rep__tag : 문제는 내가

Q(tag_rep__tag=tag.id) 

정말이 모델을 위해 무엇을 이해하지 못하는 것입니다 단일 태그가 아닌 태그 목록을 반환합니다. 그래서 나는 내가했던 것처럼 질의를 할 수 없다. 내가 tag_rep__tag을 비교할 수 있습니다 어떻게

  1. 가 나는의 검색어를 구축하기 위해 노력하고있어 태그

의 목록이 모두 포함 태그 목록 중 하나를 포함하고 있는지 확인하려면 태그 태그가있는 항목에 대한 태그 기반 태그 필터. 나는 allready 필터의 생성에 성공, 그러나 그들은 비단뱀 filter을 기반으로하고 목록을 반환 :

class TagFilter(object): 

    def __init__(self,any_tags,avoid_tags,all_tags): 
     # all tags as tag instances 
     self.any_tags = any_tags 
     self.avoid_tags = avoid_tags 
     self.all_tags = all_tags 

    def seek_any_tags(self, item): 
     for tag in self.any_tags: 
      if tag in item.tags: 
       return True 
     return False 

    def items_with_any_tags(self,items): 
     return filter(self.seek_any_tags,items) 

이 꽤 잘 작동,하지만 지금은 해당 formset에 전달하는 대가로 검색어 세트가 필요합니다. 그래서 현재의 메소드는 더 이상 작동하지 않습니다.리스트를 리턴합니다. 내가 이런 짓을 내 모델의 태그에 액세스 할 수 있도록 :

from django.contrib.contenttypes import generic 
from tagging.models import TaggedItem 

class Item(models.Model): 

    # some_fields here 

    tag_rep = generic.GenericRelation(TaggedItem) 

필터 seek_any_tags 것은 내가 allready 데이터베이스 쿼리의 형태로 다시 수있는 유일한 사람이었다. 일부 SO 게시물을 읽고 인터넷 검색의 많은 일 후에 내가이 솔루션을 내놓았다 : 그것에 대해

def seek_any_tags_qs(self,items): 

    if self.any_tags!=None: 
     q = reduce(lambda x, y: x | y, [Q(tag_rep__tag=tag.id) for tag in self.any_tags]) 
     items = items.filter(q) 

    return items 

아주 좋은, 그리고 예, 내가 조금 자랑스럽게 생각합니다. 하지만 이제는 빌드하려는 필터가 두 개 더 있습니다. avoid_any_tagsseek_all_tags. 나는 내가 seek_any_tags 필터를했던 것과 같은 방식으로하려고 노력 :

def avoid_any_tags_qs(self,items): 

    if self.avoid_tags != None: 
     q = reduce(lambda x, y: x | y, [Q(tag_rep__tag=tag.id) for tag in self.avoid_tags]) 
     items = items.exclude(q) 

    return items 


def seek_all_tags_qs(self,items): 

    if self.all_tags != None: 
     q = reduce(lambda x, y: x & y, [Q(_tags__contains=tag.name) for tag in self.seek_tags]) 
     items.filter(q) 

    return items 

avoid_any_tags가 avoid_tags의 태그 중 하나를있는 모든 항목을 제외해야합니다. 마지막 하나 인 seek_all_tags은 all_tags에 모든 태그가있는 항목 만 반환해야합니다.

그러나 avoid_any_tags은 exected로 작동하지 않습니다. avoid_tags에 태그가 있고 다른 태그는없는 요소 만 제외합니다. 항목에 avoid_tags에없는 태그가 있으면 제외되지 않습니다. 왜 안돼?

+0

이 필터는 별도의 쿼리 세트 개체에 사용됩니까? 아니면 하나 하나를 통과시키고 필터와 제외를 겹쳐서 사용하고 있습니까? – jdi

+0

나는 그 (것)들을 위로 쌓고있다, 그러나 나는 그것이 문제가 아니다고 생각한다. 나는 다른 순서로 몇 번 확인했다. 업데이트는 2 분 안에있을 것이고, 아마 핵심을 찾았을 것입니다. – marue

답변

1

나는이 (당신의 예에서) 완전히 적절한 쿼리입니다 생각하지 않습니다

Q(tag_rep__tag=tag.id) 

난 당신이 examples listed here에 가까운 다음과 같다 가정하고, 그 TaggedItem의 태그 필드 슬러그입니다 (문자열) 필드? 그래서 박쥐를 벗어나서, 이드가 슬러그 필드와 같은 곳에서 필터링하는 것은 옳지 않은 것처럼 보입니다. 또한 GenericRelation이 목록 (또는 해당 문제에 대한 쿼리 세트)을 나타내는 것이 맞습니다. 그것의 foreignkey 필드를 지정하는 TaggedItem에 대한 역 참조.그래서 당신은 할 수있을 것입니다 :

귀하의 질문에 따라 조금 따라하기 힘든 부분이 많아서, 나는 그 중 대부분을 무시하고 요약을 요약 할 것입니다. 에 관해서는

# Any of a list of strings* 
any_tags = ['foo', 'bar'] 
Item.objects.filter(tag_rep__tag__in=any_tags) 

1, 어쩌면이이 작업을 수행하는 정말 복잡한 장고 쿼리 기반의 방법이지만, 여기에 중간 단계는 클라이언트 측에있는과 접근 방법이다 "주어진 태그 목록에서 모든 태그가"

를 아이템 태그 ['a,'b']을 가지고 있으며 all_tags이 ['a','b',c'] 곳이 마지막 예에서
# items having all tags in a given list of tags 
from collections import defaultdict 

tag_vals = Item.objects.all().values_list('id', 'tags__tag').distinct() 
# produces result like: `[(1, u'bdfl'), (1, u'boom'), (2, u'bar'), ...)` 

all_tags = set(['boom', 'fizz']) 

tag_groups = defaultdict(set) 
for id_, tag in tag_vals: 
    tag_groups[id_].add(tag) 
# produces tag_groups like `{1: set([u'fizz', u'foo', u'boom']), ...}` 

item_ids = [id_ for id_,tags in d.iteritems() if tags.issubset(all_tags)] 
Item.objects.filter(id__in=item_ids) 

, 그것은 상황을 일치합니다,하지만 all_tags에보다 항목이 더 태그가 상황과 일치하지 것이지만, 사람이 할 수있다 시합. 이 중 하나의 방법을 일치하도록하려면, 해당 필터 item_ids 수정해야 할 것 :

item_ids = [id_ for id_,tags in d.iteritems() if \ 
       tags.issubset(all_tags) or tags.issuperset(all_tags)] 
Item.objects.filter(id__in=item_ids) 
+0

'item에 all_tags보다 많은 태그가 있지만 일치하는 것이 일치하는 상황은 일치하지 않습니다. - 이것은 피하기 쿼리와 정확히 일치합니다. 솔루션의 문제점은 여전히 ​​쿼리 집합 대신 목록을 반환한다는 것입니다. 그러나 약간을 명확히하는 것을 시도를를위한 감사합니다. – marue

+0

@marue : 그게 무슨 소리 죠? 내 예제 모두 queryset 결과. 이 접근법은 약간의 클라이언트 측 프로세싱을 사용하지만 최종 쿼리는 쿼리 세트라는 점에서 분명했습니다. 나는 네가 결국 내 말을 잘못 읽은 것 같아. 클라이언트 측 필터의 수정 된 버전 이었지만 여전히 쿼리를 실행합니다. 방금 예제를 업데이트하여보다 명확하게했습니다. – jdi

+0

오, 그 복잡한 질문을 묻는 결과입니다 : 당신은 복잡한 대답을 얻습니다. (당신이 이해하지 못하는) ... 죄송합니다. 나는 정말로 그 일을하는 해결책을 찾는 데 갇혀있었습니다. 당신의 대답의 요점. 내가 곧 내 솔루션을 게시 하겠지만 (지금은 작동 함), 지금 당장 귀하의 제안을 이해하게 될 것입니다. – marue

0

이 짧은 만들려면을,이 작품 코드입니다 : 난 아직도 이해하지 못하는 어떤

# does the item have any of the seek tags? 
has_seek_tags = Q(tags__id__in=self.seek_tags) 
items = items.filter(has_seek_tags).distinct() 

# does the item have any of the avoid tags?  
has_avoid_tags= Q(tags__id__in=self.avoid_tags) 
items = items.exclude(has_avoid_tags).distinct() 

# now apply the queries 
for tag_id in self.all_tags: 
    items = items.filter(has_avoid_tags) 

이다가, for 루프의 items.filter가 다음과 같지 않은 이유는 무엇입니까?

q = reduce(lambda x,y: x&y, [Q(tags__id__exact=tag_id) for tag_id in self.all_tags) 
items = items.filter(q) 

어쩌면 그게 문제 일 수 있습니다.

+0

첫 번째 예제에서는 Q 개체가 필요하지 않습니다. 당신은 하나의 진술만을하고 있습니다. 그것은 Q없이 직접 표현 될 수 있습니다. 그리고 당신의 감소를 위해, 그것은 AND가 아닌 논리적 OR이어야합니다. 아니면 당신이 감히 표현하려고 노력하는 것을 이해하지 못할 수도 있습니다. – jdi

+0

Q 개체는 절대적으로 적합합니다. 내가 그들과 너무 사랑에 빠졌던 것처럼 보였어, 그들을 쫓아 낼 시간. 두 번째 필터를 사용하면 완전히 엉망이 될 것이라고 확신합니다. 당신의 노력에 감사드립니다. – marue

관련 문제