How do I get two distinct random records using Django? I've seen questions about how to get one but I need to get two random records and they must differ.
7 Answers
The order_by('?')[:2]
solution suggested by other answers is actually an extraordinarily bad thing to do for tables that have large numbers of rows. It results in an ORDER BY RAND()
SQL query. As an example, here's how mysql handles that (the situation is not much different for other databases). Imagine your table has one billion rows:
- To accomplish
ORDER BY RAND()
, it needs aRAND()
column to sort on. - To do that, it needs a new table (the existing table has no such column).
- To do that, mysql creates a new, temporary table with the new columns and copies the existing ONE BILLION ROWS OF DATA into it.
- As it does so, it does as you asked, and runs rand() for every row to fill in that value. Yes, you've instructed mysql to GENERATE ONE BILLION RANDOM NUMBERS. That takes a while. :)
- A few hours/days later, when it's done it now has to sort it. Yes, you've instructed mysql to SORT THIS ONE BILLION ROW, WORST-CASE-ORDERED TABLE (worst-case because the sort key is random).
- A few days/weeks later, when that's done, it faithfully grabs the two measly rows you actually needed and returns them for you. Nice job. ;)
Note: just for a little extra gravy, be aware that mysql will initially try to create that temp table in RAM. When that's exhausted, it puts everything on hold to copy the whole thing to disk, so you get that extra knife-twist of an I/O bottleneck for nearly the entire process.
Doubters should look at the generated query to confirm that it's ORDER BY RAND()
then Google for "order by rand()" (with the quotes).
A much better solution is to trade that one really expensive query for three cheap ones (limit/offset instead of ORDER BY RAND()
):
import random
last = MyModel.objects.count() - 1
index1 = random.randint(0, last)
# Here's one simple way to keep even distribution for
# index2 while still gauranteeing not to match index1.
index2 = random.randint(0, last - 1)
if index2 == index1: index2 = last
# This syntax will generate "OFFSET=indexN LIMIT=1" queries
# so each returns a single record with no extraneous data.
MyObj1 = MyModel.objects.all()[index1]
MyObj2 = MyModel.objects.all()[index2]

- 1,937
- 3
- 15
- 19
-
5+1 very nice explanation and a great example! Bear in mind on most storage engines (anything but MySQL MyISAM?) it has to traverse the entire dataset to fetch the table's `count` so this can be fairly expensive too. Getting random records is a fairly expensive thing to do, generally. – adamnfish Jun 20 '11 at 10:21
-
2Yeah, I'm trying to fetch 1000 random records. This method is brutal. Untenable really. – mlissner Sep 01 '11 at 23:15
-
why not try to get all mymodel objects and use len(), like obj = MyModel.objects.all() index = randint(0,len(obj)-1) – paynestrike Apr 08 '13 at 02:05
-
1@paynestrike Sorry if you meant that as a joke - just in case: the larger the table, the worse the idea of fetching all of the objects at once. You don't really want to instantiate a billion objects so that you can select two random ones. – mikenerone May 14 '14 at 01:05
-
3@Manganeez, I meant the order_by('?') was untenable, not the solution here. Sorry to be unclear, my bad. – mlissner May 14 '14 at 04:33
-
this code will give two times the same if index1 is last – Info-Screen Jan 05 '17 at 20:53
-
@Info-Screen I don't think so - say 10 items, i.e. 0-9, so last = 9, and index1 also gets 9 per your scenario. Then index2 = randint(0, 8), so it gets a random number between 0 and 8 inclusive. Since this excludes 9, it is not possible for index1 and index2 to be equal, so they are left as-is, with 9 and some number 0-8, respectively. – mikenerone Jan 23 '17 at 00:46
-
@CryingCyclops Could you do something even more clever like hashing a uuid into, say, 1000 buckets and then randomly select one of those buckets, then do a second random to pick from objects in that bucket? (Maybe this is more clever, maybe not. But I need something very efficient.) – Evan Zamir Dec 13 '17 at 22:10
-
@EvanZamir I'm not sure what the win is for doing that. Unless, of course, your storage backend is optimized for this type of access in some way, in which case more power to ya! :) – mikenerone Dec 15 '17 at 03:40
-
1And to get `n` different random numbers, use `random.sample(range(0, last), n)`. – Pavel Vergeev Dec 15 '18 at 09:21
If you specify the random operator in the ORM I'm pretty sure it will give you two distinct random results won't it?
MyModel.objects.order_by('?')[:2] # 2 random results.

- 15,269
- 2
- 58
- 65
-
Yes this works. I was adding .get() at the end which was causing the error. – Matt McCormick Nov 13 '09 at 20:32
-
7This can have performance issues. See [Manganeez's answer](http://stackoverflow.com/questions/1731346/how-to-get-two-random-records-with-django#6405601) for details – Pierre de LESPINAY Dec 29 '11 at 09:29
-
For the future readers.
Get the the list of ids of all records:
my_ids = MyModel.objects.values_list('id', flat=True)
my_ids = list(my_ids)
Then pick n random ids from all of the above ids:
n = 2
rand_ids = random.sample(my_ids, n)
And get records for these ids:
random_records = MyModel.objects.filter(id__in=rand_ids)

- 2,478
- 18
- 35
-
2Thanks, this solution worked for me. Just a note though, I had to do `my_ids = list(my_ids)`, otherwise I got a `TypeError` from `random.sample`. I also implemented a way to handle `my_ids` having fewer records than my sample size, by doing `sample_size = min(len(my_ids), 10)`. – Jordan Jun 01 '17 at 01:18
-
This can actually be pretty expensive in data transfer and memory usage. It returns the entire list of all of the IDs from the table just so it can select 2 (or n). That can get prohibitive if your table is large. – mikenerone Jan 23 '19 at 03:58
-
I think it's not a way to go. As you pull the records again the django by default will order it again with the queryset. – root Dec 28 '19 at 14:58
Object.objects.order_by('?')[:2]
This would return two random-ordered records. You can add
distinct()
if there are records with the same value in your dataset.

- 5,701
- 6
- 32
- 29
About sampling n random values from a sequence, the random lib could be used,
random.Random().sample(range(0,last),2)
will fetch 2 random samples from among the sequence elements, 0 to last-1

- 4,513
- 9
- 31
- 51

- 39
- 3
from django.db import models
from random import randint
from django.db.models.aggregates import Count
class ProductManager(models.Manager):
def random(self, count=5):
index = randint(0, self.aggregate(count=Count('id'))['count'] - count)
return self.all()[index:index + count]
You can get different number of objects.

- 91
- 1
- 1
class ModelName(models.Model):
# Define model fields etc
@classmethod
def get_random(cls, n=2):
"""Returns a number of random objects. Pass number when calling"""
import random
n = int(n) # Number of objects to return
last = cls.objects.count() - 1
selection = random.sample(range(0, last), n)
selected_objects = []
for each in selection:
selected_objects.append(cls.objects.all()[each])
return selected_objects

- 85
- 1
- 3
- 10