How to select random record with Django ORM

This post details how I solved displaying random banner on this very site. The solution does not have any prerequisites.

Published: Sept. 8, 2020
App Store

Recently I wanted to implement more sophisticated banners on this site to promote my iOS apps. Initially I had just one hardcoded banner in the template but that was not going to work for multiple banners.

The part that took the longest was to display random banner on a page. This involves mainly getting random record (or entity if you will) from your database. I have found quite a few solutions but they were either too complicated or relied on some preconditions like you had to have a table with no deleted records + sequential ids and then you could use something like last id or count and select random number from that.

My solution does not have any requirements but could be problematic if you needed to select from possibly 100s of thousands or more records.

Anyway, let's move to code. My Banners can be toggled off with property is_active so I am using Django ORM filter together with values to pull out all the available ids. Like this:

banner_ids = Banner.objects.filter(is_active=True).values('id')

Once that is done, we can check if the result is not empty and if not, utilize choice form random module to pick random element.

import random

if len(banner_ids) > 0:
    random_id = random.choice(banner_ids)["id"]

    try:
        banner = Banner.objects.get(pk=random_id)
    except Banner.DoesNotExist:
        banner = None

Because the banners_ids is basically "array" of key-value pairs, there is ["id"] subscript to pull out just the number. Once we have random id, we can use that to try to get record with the get method.

And that is all!

I think this is pretty good for my usecase and for many other assuming you don't have tons of data to work with.

I have a few banners and the pages are cached so I don't almost have to even think about performance.

Note on memory usage

I have been recently informed by a reader that the above method might be memory intensive in certain situations. Theirs was Raspberry Pi and presumably large number of records.

A solution for this situation can be to use order_by("?")[0] which does random ordering at the database level and uses less memory.

Bluesky logo

Follow on Bluesky to not miss new posts

Filip Němeček profile photo

WRITTEN BY

Filip Němeček Mastodon

iOS blogger and developer with interest in Python/Django.

iOS blogger and developer with interest in Python/Django.