In django, how to write a query that selects all possible combinations of four integers?
-
06-07-2019 - |
Question
I'm writing a Game Website, where the draw is a series of four digits. e.g 1234
I"m trying to write a query in django that will select all winners based on the four digits entered. winners are any combination of the same numbers or the same combination, 1 2 3 4, 2 3 1 4, 4 1 3 2 are all winners.
how is the most efficient way to write this query.
--------------------- Edit, sorry for not providing model samples here there are below: -----------
class Draw(models.Model):
digit1 = models.PositiveSmallIntegerField(null=True,blank=True)
digit2 = models.PositiveSmallIntegerField(null=True,blank=True)
digit3 = models.PositiveSmallIntegerField(null=True,blank=True)
digit4 = models.PositiveSmallIntegerField(null=True,blank=True)
draw_date = models.DateTimeField()
closed = models.BooleanField()
winner = models.BooleanField()
def __unicode__(self):
return "Draw For Week Ending %s" %(self.draw_date)
def get_absolute_url(self):
return "/draw/%s/" % (self.draw_date)
def save(self, force_insert=False, force_update=False):
if self.digit1 and self.digit2 and self.digit3 and self.digit4:
#check if there are winners
try:
winners = Ticket.objects.filter(draw=self.id,digit1=self.digit1,digit2=self.digit2,digit3=self.digit3,digit4=self.digit4)
self.winner = True
except Ticket.DoesNotExist:
self.winner = False
#close & save draw/winners
self.closed = True
# Add new Draw for following week.
new_date = self.draw_date + datetime.timedelta(hours=168)
new_draw= Draw(draw_date=new_date)
new_draw.save()
super(Draw, self).save(force_insert, force_update) # Call the "real" save() method.
class Serial(models.Model):
serial = models.CharField(max_length=4)
closed = models.BooleanField(unique=False)
def __unicode__(self):
return "%s" %(self.serial)
def get_absolute_url(self):
return "/draw/serial/%s/" % (self.serial)
class Ticket(models.Model):
draw = models.ForeignKey(Draw)
digit1 = models.PositiveSmallIntegerField()
digit2 = models.PositiveSmallIntegerField()
digit3 = models.PositiveSmallIntegerField()
digit4 = models.PositiveSmallIntegerField()
date = models.DateField(auto_now_add=True,editable=False)
active = models.BooleanField(default=True)
serial_used = models.ForeignKey(Serial,related_name="ticket_serial_used")
def __unicode__(self):
return "#: %s - %s" %(self.id,self.draw)
def get_absolute_url(self):
return "/ticket/%s/" % (self.id)
def save(self, force_insert=False, force_update=False):
if self.serial_used:
serial = Serial.objects.get(pk=self.serial_used.id)
serial.closed = True
serial.save()
super(Ticket, self).save(force_insert, force_update) # Call the "real" save() method.
Solution
I'd advise adjusting the code to save the digits so that they are saved in sorted order. E.g. if the user puts in "5262" then it should store that as "2256". Then, when you select a winning set of digits, you can sort those, and filter by simple equality. This will perform much, much better than trying to check for all possible combinations.
If you need the unsorted selection for other purposes, then add a new field to the model sortedDigits
or something so that you have it to compare against.
OTHER TIPS
Code:
from itertools import permutations
winning_numbers = "1234"
winning_combinations = map(lambda v: "".join(v), list(permutations(winning_numbers, 4)))
winners = GamesPlayed.objects.filter(numbers__in=winning_combinations)
Assuming GamesPlayed is the model object for all games played, with a text field numbers containing the four selected numbers in the format NNNN
.
If you're on Python 2.5 itertools
does not have permutations
. The docs have an implementation you can use: http://docs.python.org/library/itertools.html#itertools.permutations
Is the order of the numbers important?
If not, you could sort the digits for tickets and draws in ascending order, then use your code
winners = Ticket.objects.filter(draw=self.id,digit1=self.digit1,digit2=self.digit2,digit3=self.digit3,digit4=self.digit4)
As an aside, Your try... except block won't catch the situation when there are no winners. The DoesNotExist
exception is thrown by the get
method (see docs).
If there isn't a winning ticket, the filter
method will return an empty queryset, but not raise an error. You can then check whether there are winners using an if statement.
if winners
# there are winners
self.winner = True
else:
# there are not winners
self.winner = False