2015-09-11 2 views
1

У меня есть модель, у которой есть поле тегов, которое представляет собой список тегов, разделенных запятыми. Учитывая список строк тегов, я построил запрос, который находит экземпляры моей модели, имеющие хотя бы один из тегов. Я это работает так:Сортировка по количеству совпадений строк в Django queryset

tag_list = ["tag1", "tag2"] 

MyModel.objects.filter(
    reduce(
     lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tag_list])) 

Но теперь я хочу, чтобы отсортировать QuerySet в порядке убывания числа, соответствующего теги каждый элемент имеет. Я пытаюсь найти правильный способ аннотировать поле, которое будет содержать счетчик совпадений, но не имел большой удачи. Любые указатели?

+0

Я думаю, что это было бы более _efficient_ сделать сортировку в питона, а не базы данных. После того, как вы получите «список» объектов, имеющих хотя бы один тег, вы можете отсортировать его с помощью атрибута 'MyModel.tags.count()', переданного в сравнение сортировки, - это будет похоже на сортировку списка объектов на основе в целочисленном поле – kicker86

ответ

0

Я думаю, что было бы более эффективным, чтобы выполнить сортировку в python, а не в базе данных. После того, как вы получите list объектов, которые имеют по крайней мере один тег, вы можете отсортировать его с помощью атрибута MyModel.tags.count() передается в сортировочном сравнение - это было бы так же, как сортировка списка объектов на основе целого поля

# your query 
objects = MyModel.objects.filter('...') 

# sort in place 
objects.sort(
    # set the sorting comparison to tag count 
    key = lambda obj: obj.tags.count(), 
    # if you want a descending comparison, highest tag count first 
    # reverse = True 
) 

Я говорю, что это было бы больше эффективным, потому что я предполагаю, что вы собираетесь использовать все объекты в любом случае, вместо того чтобы сказать, просто получите первый (с наивысшим количеством тегов). Поэтому вместо того, чтобы делать базу данных, выполняйте сравнения, вы можете сделать это в самом python.

+0

объекты - это запрос, поэтому он не будет иметь функцию сортировки. Кроме того, мне нужен мой результат для набора запросов, поскольку я переопределяю get_queryset в подклассе ListView. Наконец, как бы obj.tags.count() дал мне количество совпадающих тегов? – user857159

0

я понял, (сложный) раствор:

from django.db.models import Case, ExpressionWrapper, IntegerField, Q, Value, When 

tag_list = ["tag1", "tag2"] 

qs = MyModel.objects.filter(
    reduce(
     lambda x, y: x | y, [Q(tags__icontains=tag) for tag in tag_list])) 

check_matches = map(
    lambda x: Case(
     When(Q(tags__icontains=x), then=Value(1)), 
      default=Value(0)), 
    tag_list) 
count_matches = reduce(lambda x, y: x + y, check_matches) 
qs = qs.annotate(
    matches=ExpressionWrapper(
     count_matches, 
     output_field=IntegerField())) 
qs = qs.order_by('-matches') 
Смежные вопросы