python - How to pull a random record using Django's ORM?

ID : 20067

viewed : 28

Tags : pythondjangodjango-modelspython

Top 5 Answer for python - How to pull a random record using Django's ORM?

vote vote

100

Simply use:

MyModel.objects.order_by('?').first() 

It is documented in QuerySet API.

vote vote

87

Using order_by('?') will kill the db server on the second day in production. A better way is something like what is described in Getting a random row from a relational database.

from django.db.models.aggregates import Count from random import randint  class PaintingManager(models.Manager):     def random(self):         count = self.aggregate(count=Count('id'))['count']         random_index = randint(0, count - 1)         return self.all()[random_index] 
vote vote

77

The solutions with order_by('?')[:N] are extremely slow even for medium-sized tables if you use MySQL (don't know about other databases).

order_by('?')[:N] will be translated to SELECT ... FROM ... WHERE ... ORDER BY RAND() LIMIT N query.

It means that for every row in table the RAND() function will be executed, then the whole table will be sorted according to value of this function and then first N records will be returned. If your tables are small, this is fine. But in most cases this is a very slow query.

I wrote simple function that works even if id's have holes (some rows where deleted):

def get_random_item(model, max_id=None):     if max_id is None:         max_id = model.objects.aggregate(Max('id')).values()[0]     min_id = math.ceil(max_id*random.random())     return model.objects.filter(id__gte=min_id)[0] 

It is faster than order_by('?') in almost all cases.

vote vote

61

Here's a simple solution:

from random import randint  count = Model.objects.count() random_object = Model.objects.all()[randint(0, count - 1)] #single random object 
vote vote

51

You could create a manager on your model to do this sort of thing. To first understand what a manager is, the Painting.objects method is a manager that contains all(), filter(), get(), etc. Creating your own manager allows you to pre-filter results and have all these same methods, as well as your own custom methods, work on the results.

EDIT: I modified my code to reflect the order_by['?'] method. Note that the manager returns an unlimited number of random models. Because of this I've included a bit of usage code to show how to get just a single model.

from django.db import models  class RandomManager(models.Manager):     def get_query_set(self):         return super(RandomManager, self).get_query_set().order_by('?')  class Painting(models.Model):     title = models.CharField(max_length=100)     author = models.CharField(max_length=50)      objects = models.Manager() # The default manager.     randoms = RandomManager() # The random-specific manager. 

Usage

random_painting = Painting.randoms.all()[0] 

Lastly, you can have many managers on your models, so feel free to create a LeastViewsManager() or MostPopularManager().

Top 3 video Explaining python - How to pull a random record using Django's ORM?

Related QUESTION?