繁体   English   中英

将Django查询集随机化一次,然后遍历它

[英]Randomize a Django queryset once, then iterate through it

我正在编写一个应用程序,允许人们比较不同的主题标签对。

模型:

class Competitors(models.Model):
    tag1 = models.ForeignKey('Hashtag', related_name='+')
    tag2 = models.ForeignKey('Hashtag', related_name='+')
    votes = models.PositiveIntegerField(default=0, null=False)

视图:

def compare_hashes(request, i=None):
    i = i or 0
    try:
        competitors = Competitors.objects.order_by('?')[i]
    except IndexError:
        return render(request, 'hash_to_hash.html',
            {'tag1': '', 'tag2': '', i: 0, 'done': True})

    if request.method == 'POST':
        form = CompetitorForm(request.POST)
        if form.is_valid():
            if "yes" in request.POST:
                competitors.votes += 1
                competitors.save()
        i += 1
        return render(request, 'hash_to_hash.html',
                  {'tag1': competitors.tag1, 'tag2': competitors.tag2, i: i, 'done': False})

    else:
        return render(request, 'hash_to_hash.html',
                  {'tag1': competitors.tag1, 'tag2': competitors.tag2, i: i, 'done': False})

我想要做的是,每个访问者,随机化竞争对手的对象的顺序,然后迭代该随机列表。

问题:

  1. 除了o bjects.order_by('?')之外,还有什么更好的随机化方法? 我正在使用MySQL,我在这里看到了一些关于order_by('?') + MySQL = SLOOOOOOOW的内容。 给出了一些建议,我可以很容易地实现一些东西(我正在考虑一下random.shuffle(Competitors.objects.all()) ),但是我不知道我把它放在哪里,这引出了我的第二个问题......
  2. 如何确保随机化仅发生一次? 我不想让他们一遍又一遍地审查同一对人,并且我不想通过让一些对随机出现不止一次来甩掉我的结果。 我希望每个人都能看到相同的清单,只是按照不同的顺序。

我怀疑答案在于经理类,但是,实际上,这一切都归结为我对Django所谓的调用缺乏了解。

(我也有一个问题,结果似乎没有保存到我的数据库,但这是一个不同的,可能更容易解决的问题。)

要保持一致的随机顺序,您应该通过种子随机排序,并将种子存储在会话中。 不幸的是你不能用纯粹的django orm来做这件事,但是使用mysql它是微不足道的:

import random
from django.conf import settings

# there might be a better way to do this...
DATABASE_ENGINE = settings.DATABASES[settings.DATABASES.keys()[0]]['ENGINE'].split('.')[-1]

def compare_hashes(request, i=None):
    competitors = Competitors.objects.all()

    if DATABASE_ENGINE == 'mysql':
        if not request.session.get('random_seed', False):
            request.session['random_seed'] = random.randint(1, 10000)
        seed = request.session['random_seed']
        competitors = competitors.extra(select={'sort_key': 'RAND(%s)' % seed}).order_by('sort_key')

    # now competitors is randomised but consistent for the session
    ...

我怀疑在大多数情况下表演会成为一个问题; 如果你最好的选择是在你的数据库中创建一些索引的sort_key列,这些列会定期使用随机值更新,并在会话中的其中一列上订购。

尝试了格雷格关于PostgreSQL的答案并得到了一个错误,因为那里没有种子的随机函数。 经过一番思考后,我采用了另一种方式将这项工作交给Python,后者更喜欢这样的任务:

def order_items_randomly(request, items):
    if not request.session.get('random_seed', False):
        request.session['random_seed'] = random.randint(1, 10000)
    seed = request.session['random_seed']
    random.seed(seed)
    items = list(items)
    random.shuffle(items)
    return items

在我的1.5k项目查询集上工作得足够快。

PS并且因为它将查询集转换为列表,所以最好在分页之前运行此函数。

我的解决方案主要基于Greg上面的精彩建议:

视图:

def compare_hashes(request, i=0):
    i = int(i)
    competitors = Competitors.objects.all()
    DATABASE_ENGINE = settings.DATABASES['default']['ENGINE'].split('.')[-1]
    if DATABASE_ENGINE == 'mysql':
        if not request.session.get('random_seed',False):
            ints = xrange(10000)
            request.session['random_seed'] = sample(ints,1)[0]
        seed = request.session['random_seed']
        competitors = competitors.extra(select={'sort_key': 'RAND({})'.format(seed)})
        randomized_competitors = competitors.order_by('sort_key')
    try:
        chosen_competitor = randomized_competitors[i]
    except IndexError:
        return render(request, 'hash_to_hash.html',
        {'tag1': '', 'tag2': '', i: 0, 'done': True})

    if request.method == 'POST':
        form = CompetitorForm(request.POST)
        if form.is_valid():
            if "yes" in request.POST:
                competitors.votes += 1
                competitors.save()
            i += 1
    return render(request, 'hash_to_hash.html',
              {'tag1': chosen_competitor.tag1, 'tag2': chosen_competitor.tag2, 'action':'/hash/{}'.format(i), 'done': False})

模板(使用Django-bootstrap-toolkit ,仍然需要一些工作):

{% extends 'base.html' %}
{% load bootstrap_toolkit %}
{% block title %}Title{% endblock %}
{% block big_title %}Title{% endblock %}
{% block main-content %}
    <h3>Hash-to-Hash</h3>
    {% if done %}
    <div class="row-fluid">
        <div class="span8">
            <h4>You're Done!</h4>
            <p>Thanks so much!</p>
        </div>
    </div>
    {% else %}
        <div class="row-fluid">
            <div class="span6" id="tag1">
                <h4>{{ tag1.text }}</h4>
            </div>
            <div class="span6" id="tag2">
                <h4>{{ tag2.text }}</h4>
            </div>
        <div class="span8">
            <form action="{{ action }}" method="post">
           {% csrf_token %}
           <!-- {{ form|as_bootstrap }} -->
           {% bootstrap_form form layout="vertical" %}
           <div class="form-actions">
               <button type="submit" class="btn btn-success" name="yes">
                   YES, I think these two tags are co-referential
               </button>
               <button type="submit" class="btn btn-danger" name="no">
                   NO, I don't think these two tags are co-referential
               </button>
            </div>
            </form>
        </div>
    </div>
    {% endif %}
{% endblock %}

URLconf看起来像这样: url(r'^hash/(\\d*)$', compare_hashes)

再次感谢!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM