4

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

模型:

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. 如何确保随机化只发生一次?我不想让人们一遍又一遍地查看相同的配对而让他们感到厌烦,我不想让一些配对随机出现不止一次来影响我的结果。我希望每个人都看到相同的列表,只是顺序不同。

我怀疑答案在于 Manager 类,但实际上,这一切都归结为我对 Django 何时调用的内容缺乏了解。

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

4

3 回答 3

4

在 PostgreSQL 上尝试了 Greg 的回答并得到了一个错误,因为那里没有带有种子的随机函数。经过一番思考,我换了一条路,把这份工作交给了 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并且由于它将查询集转换为列表,因此最好在分页之前运行此功能。

于 2013-11-28T21:06:35.517 回答
2

为了保持一致的随机顺序,您应该按种子随机排序,种子存储在会话中。不幸的是,你不能用纯 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 列,这些列会使用随机值定期更新,并为会话中的一个排序。

于 2013-05-17T00:32:28.717 回答
0

我的解决方案,主要基于 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)

再次感谢!

于 2013-05-17T01:51:49.267 回答