Django 的 F() 表达式奇怪行为

Posted

技术标签:

【中文标题】Django 的 F() 表达式奇怪行为【英文标题】:F() Expression strange behavior with Django 【发布时间】:2015-08-05 16:02:17 【问题描述】:

我有这个模板标签,它最终返回一个“活动”广告列表(检查活动字段是否为 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 无论如何。

知道为什么会发生这种奇怪的行为吗?如果需要,我可以提供更多信息。

【问题讨论】:

random_camps 中有什么内容?您的广告系列是否有可能不止一次出现在其中? 我已更新以显示 random_camps 的完整代码。 我们已经用多个广告系列甚至只有一个广告系列对其进行了测试。即使 random_camps 中只有一个对象,它仍然会将整数增加 2。 【参考方案1】:

random.choice 不保证生产不重复的项目。

import random

random_camps = random.sample(current_campaigns, amount)

是去这里的路。

更新 如果您担心速度,this question 解决了 postgres 中的快速随机行选择问题。

【讨论】:

这会引发属性错误,不是吗? Shuffle 不是 'QuerySet' 的属性 有趣,这效率如何?我的意思是,如果 current_campaigns 最终成为一个大型查询集? 等等,这如何保证生产不重复的项目? 在内部它使用与任何其他 random.* 函数相同的随机数生成器,所以我希望它与手动生成序列并检查它的不可重复性一样快(例如使用set() 已生成的项目)。 随机播放(就像任何其他基于伪随机的方法一样)还有另一个问题 - 由于随机数的非重复序列长度有限,它不会达到所有可能的排列。

以上是关于Django 的 F() 表达式奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

具有奇怪行为的正则表达式:将字符串与反向引用匹配以允许转义以及单引号和双引号

Django F()表达式

django F表达式生成的SQL查询

Django F 表达式加入字段

使用 F 和 Q 表达式进行 Django 模型过滤

如何使用 django F 表达式仅过滤日期时间字段的日期