问题
I have those two models (simplified):
class Place(OrderedModel):
name = models.CharField(max_length=100)
class Team(models.Model):
name = models.CharField(max_length=100)
places = models.ManyToManyField(Place, blank=True)
Say that there are ten instances of Place. But only five instances of Team. I want to return a list of booleans in the order of entries in Place, where True means that Team.places contains that place and False (obviously) means that it doesn't. The Team.places field has no particular order.
In the following example the Team have the instances of the first four Place objects and the last one in it's places field:
[True, True, True, True, False, False, False, False, False, True]
I solved this with this method on the Team model:
class Team(models.Model):
...
def done(self):
"""
Returns a list of Booleans, one for each place
in order. True means that the team has found the
place. False, that they have not.
"""
places = Place.objects.all()
return [p in self.places.all() for p in places]
It works but seems highly inefficient. It make (I believe) two different SQL queries and then a list comprehension. Is there a more efficient way to solve this?
Solved
I ended up doing this:
def done(self):
"""
Returns a list of Booleans, one for each place
in order. True means that the team has found the
place. False, that they have not.
"""
sql = '''SELECT P.id, (TP.id IS NOT NULL) AS done
FROM qrgame_place P
LEFT OUTER JOIN qrgame_team_places TP
ON P.id = TP.place_id AND TP.team_id = %s'''
places = Place.objects.raw(sql, [self.id])
for p in places:
yield bool(p.done)
回答1:
You need this SQL query:
SELECT P.id, (TP.id IS NOT NULL) AS done
FROM myapp_place P
LEFT OUTER JOIN myapp_team_places TP
ON P.id = TP.place_id AND TP.team_id = %s
(You'll also need to add an ORDER BY clause to return the Place objects in the order you want, but since I can't see that part of your model, I can't tell what that clause ought to look like.)
I don't know how to express this query using Django's object-relational mapping system, but you can always run it using a raw SQL query:
>>> sql = ''' ... as above ... '''
>>> places = Place.objects.raw(sql, [team.id])
>>> for p in places:
... print p.id, bool(p.done)
...
1 True
2 True
3 False
回答2:
A more optimal solution would be:
def done(self):
places = Place.objects.values_list('pk', flat=True)
team_places = self.places.values_list('pk', flat=True)
return [place in team_places for place in places]
来源:https://stackoverflow.com/questions/15899701/optimise-two-sql-queries-and-a-list-comprehension