2012-06-03 8 views
1

Предположим, что у меня есть QuerySet, который возвращает 10 объектов, 3 из которых будут отображаться в следующих положениях:псевдослучайного упорядочение в Джанго QuerySet

[ display 1 position ]  [ display 2 position ]  [ display 3 position ] 

модель, представляющая ее следующим образом:

class FeaturedContent(models.Model): 
    image = models.URLField() 
    position = models.PositiveSmallIntegerField(blank=True, null=True) 

где позиция может быть либо 1, 2, 3, либо неуказанная (Null).

Я хочу иметь возможность заказать QuerySet случайным образом ЗА ИСКЛЮЧЕНИЕМ объектов с указанной позицией. Тем не менее, я не могу заказать его, выполнив:

featured_content = FeaturedContent.objects.order_by('-position', '?') 

Потому что, если бы я был один пункт, который имел position = 2, и все остальные элементы были Null, то этот пункт будет отображаться в положении 1 вместо положения 2.

Как бы я сделал этот заказ?

Думая об этом, возможно, было бы лучше иметь данные в виде Словаре вместо списка, что-то вроде:

`{'1': item or null, '2': item or null, '3': item or null, '?': [list of other items]}` 
+0

Вы должны действительно избегайте случайного упорядочения в вашей базе данных (как также указывает [django documentation] (https://docs.djangoproject.com/en/dev/ref/models/querysets/#order-by), поскольку она имеет тенденцию быть действительно медленный! –

+0

Тогда какой лучший способ сделать то, что мне нужно сделать выше? – David542

ответ

0

Я бы пост-обработки его, делая сортировки слиянием между упорядоченная и неупорядоченная записей.

EDIT:

начал генератора для этого:

def posgen(posseq, arbseq, posattr='position', startpos=1): 
    posel = next(posseq) 
    for cur in itertools.count(startpos): 
    if getattr(posel, posattr) == cur: 
     yield posel 
     posel = next(posseq) 
    else: 
     yield next(arbseq) 

Обратите внимание, что существует множество состояний ошибки, возможное в этом коде (подсказка: StopIteration).

+0

Спасибо, не могли бы вы показать, как это будет сделано? – David542

0

Если вы просто хотите итерации по запросу, у вас могут быть два запроса, заказать их и связать их.

import itertools 

qs1 = FeaturedContent.objects.filter(position__isnull=False).order_by('-position') 
qs2 = FeaturedContent.objects.filter(position__isnull=True).order_by('?') 
featured_content = itertools.chain(qs1, qs2) 
for item in featured_content: 
    #do something with qs item 
    print item 

Upadate:

Поскольку вы просите, чтобы убедиться, что положение определяет порядок и «пустые» пробелы заменяются случайным образом элементами с нулевыми позициями. Если признакам список вы хотите получить не слишком большой, 20 в этом случае

featured = [] 
rands = [] 
for i in xrange(1, 20): 
    try: 
     x = FeaturedContent.objects.get(position=i) # assuming position is unique 
    except FeaturedContentDoesNotExist: 
     if not rands: 
      rands = list(FeaturedContent.objects.filter(position__isnull=True).order_by('?')[:20] 
     x = rands[0] 
     rands = rands[1:] 
    featured.append(x) 
+0

+1 для того, чтобы быть быстрее =), однако -1 для него не соответствует требованию, чтобы позиция w/позиция 2 находилась в позиции 2, когда другие элементы имеют нулевую позицию – okm

+0

Правильно, это не делает ничего с точки зрения заказа как указано. – David542

0

Если вы используете дб бэкенд, который делает случайный порядок эффективно вы можете сделать это следующим образом:

# This will hold the result 
featured_dict = {} 

featured_pos = FeaturedContent.objects.filter(position__isnull=False).order_by('-position') 
featured_rand = FeaturedContent.objects.filter(position__isnull=True).order_by('?') 

pos_index = 0  
rand_index = 0 

for pos in range(1, 4): 
    content = None 

    if pos_index < len(featured_pos) and featured_pos[pos_index].position == pos: 
     content = featured_pos[pos_index] 
     pos_index += 1 

    elif rand_index < len(featured_rand): 
     content = featured_rand[rand_index] 
     rand_index += 1 

    featured_dict[str(pos)] = content 

# I'm not sure if you have to check for a valid index first before slicing 
featured_dict['?'] = featured_rand[rand_index:]