Randomize the Django job once, then do it again

I am writing an application that allows people to compare different pairs of hashtags.

Model:

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

View:

 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}) 

I want each visitor to randomize the ordering of competitors' objects, and then iterate over this randomized list.

Questions:

  • What is the best way to randomize things besides o bjects.order_by('?') ? I use MySQL and I saw some things here about how order_by('?') + MySQL = SLOOOOOOOW. Several suggestions were suggested, and I could easily implement something (I thought something like random.shuffle(Competitors.objects.all()) ), but I'm not sure where I would put it, which leads me to my second question ...
  • How can I make sure that randomization only happens once? I do not want to miss people, forcing them to review the same pairs again and again, and I do not want to discard my results when several pairs accidentally appear more than once. I want everyone to see the same list, only in different orders.

I suspect that the answer lies in the manager class, but, really, it all comes down to my lack of knowledge of what Django causes when.

(I also have a problem where the results do not seem to be stored in my db, but this is a problem that may be more easily resolved).

+4
source share
3 answers

To maintain consistent random order, you must order a random seed with seed stored in the session. Unfortunately, you cannot do this with pure django orm, but with mysql this is trivial:

 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 ... 

I doubt that performance will be a problem in most situations; if you are best off creating some sort_key indexed columns in your database that are periodically updated with random values ​​and sorted by one of them for the session.

+2
source

I tried Greg's answer on PostgreSQL and got an error because there is no random function with a seed there. After some thought, I went the other way and gave this work to Python, which most likes these tasks:

 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 

Works pretty fast on my 1.5k query set.

PS And since it converts the request into a list, it is better to run this function immediately before paging.

+2
source

My solution, based mainly on Greg’s wonderful suggestion above:

View:

 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}) 

Template (which uses Django-bootstrap-toolkit and still needs some work):

 {% 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 %} 

The URLconf looks like this: url(r'^hash/(\d*)$', compare_hashes)

Thanks again!

0
source

Source: https://habr.com/ru/post/1481262/


All Articles