1

Trying to get a random object from the database with an id. Some ids in the database table are missing, for example, 1, 2, 4, 5, 6, 19, 20... so I need to make sure I don't get an error when I try to get an object.

This seems to be working. Is there a better way.

def get_random_title():
  title_count = OriginalTitle.objects.count()
  random_id = random.randrange(1, title_count)
  random_title_obj = None

  while random_title_obj is None:
    try:
      random_title_obj = OriginalTitle.objects.get(id=random_id)
    except ObjectDoesNotExist:
      continue
  return random_title_obj.title

def play(request):
  random_title = get_random_title()
  context = {
    'original_title': random_title
  }
  return render(request, 'game/game.html', context)
     
ntf
  • 169
  • 1
  • 2
  • 17

4 Answers4

2

You can use random.sample() on a QuerySet object. or change the ordering of rows.

OriginalTitle.objects.order_by('?')[:1]

  • Thank you. Would you mind showing me how to put that in my get_random_title() function by writing out the whole function. For example, do I still need the while loop and try/except if I use that line? – ntf Mar 06 '22 at 16:49
  • 1
    def get_random_title(): return OriginalTitle.objects.order_by("?").first().title – Jimmy Pells Mar 06 '22 at 16:57
1

Using .order_by('?') is expensive. Indeed, as the documentation says:

Note: order_by('?') queries may be expensive and slow, depending on the database backend you’re using.

For most databases, it will annotate a random number to each record and then order by that number, making it linear in the size of the table.

You can return a sample of the database with:

def get_random_title():
  title_count = OriginalTitle.objects.count()
  idx = random.randrange(0, title_count)
  random_title_obj = OriginalTitle.objects.all()[idx].title

This will make two queries, but for large tables can outperform an .order_by('?').

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
0

# edit your get_random_title
def get_random_title():
  random_title_obj = OriginalTitle.objects.order_by('?')[0]
  return random_title_obj.title

please read more details in this answer

sad
  • 89
  • 1
  • 4
0

Ok. Thank you for the responses. I got rid of the get_random_title() function and just put the one line in the play() view

def play(request):
  random_title = OriginalTitle.objects.order_by("?").first().title
  context = {
    'original_title': random_title
  }
  return render(request, 'game/game.html', context)

One note to anyone who uses this answer: I see now in the docs that it says, "Note: order_by('?') queries may be expensive and slow, depending on the database backend you’re using."

ntf
  • 169
  • 1
  • 2
  • 17