F () Выражение странного поведения в Django

У меня есть этот шаблонный тег, который в конечном итоге возвращает список "активных" рекламных объявлений (проверяет, является ли Кампания с активным полем True, затем извлекает объявления из Кампании с помощью набора запросов)

@register.assignment_tag
def get_current_campaigns(amount):

    # Get all the campaigns that are active 
    current_campaigns = Campaign.objects.filter(active=True)

    current_campaigns_count = current_campaigns.count()

    # To avoid the list index being out of range and throwing an IndexError
    # We reduce the amount to match the amount of rows in the model if the
    # amount of rows is less than the amount being requested.
    if amount > current_campaigns_count:
        amount = current_campaigns_count

    # Select active campaigns randomly
    random_camps = []
    for i in range(amount):
        random_camps.append(random.choice(current_campaigns))

    # prepare all the ads to return 
    output = []
    for campaign in random_camps:
        # get all the ads that a campaign has 
        ads = campaign.advertisement_set.all()
        # now select one randomly
        ad = random.choice(ads)
        # hand it to output 
        output.append(ad)
        # mark that this campaign has been seen
        campaign.impressions = F('impressions') + 1
        campaign.save()
        # checks and sets if the campaign is still active
        campaign.check_active()

    return output

И вот модель, которая идет с ним:

class Campaign(models.Model):
    ''' Represents an Advertisement Campaign '''
    title = models.CharField(max_length=50, blank=True, verbose_name='Campaign Title')
    impressions = models.IntegerField()
    impression_limit = models.IntegerField()
    created = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=False)

    def check_active(self):
        ''' Checks if the Campaign is currently active '''
        if self.impressions >= self.impression_limit:
            self.active = False
            self.save()

Странный момент: каждый раз, когда я захожу на страницу, на которой размещена реклама, а затем проверяю ее в админке, количество показов объекта увеличивается на 2 (должно быть 1) и помечается как False, даже если это if self.impressions >= self.impression_limit неверно, он по-прежнему каким-то образом изменяет активное поле на False независимо.

Есть какие-нибудь подсказки, почему происходит такое странное поведение? При необходимости я могу предоставить дополнительную информацию.


person ApathyBear    schedule 23.05.2015    source источник
comment
Что в random_camps? Возможно ли, что ваша кампания присутствует там более одного раза?   -  person kylieCatt    schedule 23.05.2015
comment
Я обновился, чтобы показать, что такое random_camps, с полным кодом.   -  person ApathyBear    schedule 23.05.2015
comment
Мы протестировали его с несколькими кампаниями и даже с одной единственной кампанией. Даже если в random_camps есть только один объект, он все равно увеличивает целое число на 2.   -  person Llanilek    schedule 23.05.2015


Ответы (1)


random.choice не гарантирует создание неповторяющихся элементов.

import random

random_camps = random.sample(current_campaigns, amount)

это путь сюда.

Обновление. Если вас беспокоит скорость, этот вопрос касается быстрого случайного выбора строк в postgres.

person axil    schedule 24.05.2015
comment
Это вызовет ошибку атрибута, не так ли? Shuffle не является атрибутом QuerySet - person Llanilek; 24.05.2015
comment
Интересно, насколько это было бы эффективно? Я имею в виду, если, например, current_campaigns окажется большим набором запросов? - person Llanilek; 24.05.2015
comment
и, что более важно, как это гарантирует создание неповторяющихся элементов? - person Llanilek; 24.05.2015
comment
Внутри он использует тот же генератор случайных чисел, что и любая другая функция random.*, поэтому я ожидаю, что он будет столь же быстрым, как создание последовательности вручную и проверка ее на неповторяемость (например, с использованием set() уже сгенерированных элементов). - person axil; 24.05.2015
comment
Перемешивание (как и любой другой псевдослучайный подход) имеет еще одну проблему - из-за ограниченной длины неповторяющейся последовательности случайных чисел она не достигает всех возможных перестановок. - person axil; 24.05.2015
comment
choice выбирает любой элемент из последовательности, не обращая внимания на то, что было выбрано ранее. Это приводит к повторению пунктов. shuffle, с другой стороны, генерирует перестановку. Это гарантирует, что каждый элемент из начальной последовательности будет в результате и будет только одна его копия. Это определение перестановки. - person axil; 24.05.2015
comment
итерация по сумме и добавление перемешанного списка в список дает нам [Нет, Нет], если количество = 2. Не могли бы вы показать в своем примере, как перемешивание будет работать в этом примере? потому что запуск в случайном порядке в списке просто изменяет порядок в списке. - person Llanilek; 24.05.2015
comment
исправлено, посмотрите сейчас - person axil; 24.05.2015