views:

887

answers:

4

I have a basic FK to user, call it owner

class Baz(models.Model):

    owner = models.ForeignKeyField(User)


    ....
    ....

Now, with a queryset of Baz's, is there something that I can chain that will give me only one Baz per owner?

A: 

EDIT: This has a higher chance of working:

CheckIn.objects.extra(where=['checkins_checkin.id = (SELECT MAX(checkins_checkin.id) FROM checkins_checkin temp, auth_user WHERE auth_user.id = temp.id AND temp.user_id = checkins_checkin.user_id)',]).count()
shanyu
CheckIn.objects.extra(where=['checkins_checkin.id = (SELECT checkins_checkin.id FROM checkins_checkin temp, auth_user WHERE auth_user.id = temp.id ORDER BY id DESC LIMIT 1)',]).count()Out[4]: 18In [5]: CheckIn.objects.all().count()Out[5]: 18
skyl
there are only 2 owners in this test dataset but I get back all 18 objects with that query.
skyl
You're right, I forgot to add the user criteria in the where clause. I updated my answer accordingly.
shanyu
+1  A: 

The function you are truly looking for is GROUP BY. However, Django does not typically support building querysets that do not directly output model instances. In this situation, you have two approachs:

Baz.objects.values('owner').distinct()

This will net you each distinct owner, but not the Baz object itself.

Baz.objects.filter(pk__in=Baz.objects.values('owner').distinct())

The above will perform a subquery (at least in MySQL) and should give the intended results, but isn't the most efficient way to retrieve it.

Lastly, since aggregates have been added, it may be possible for you to write a custom aggregate class which would work as a kind of "Distinct" and simply "GROUP BY ".

David Cramer
your second query yields []....
skyl
Ah ya the subquery isn't quite right, use the following group by whichi s shown below.
David Cramer
+1  A: 

I believe the question is how to run the equivalent of this:

SELECT * FROM myapp_baz GROUP BY owner_id;

Which will return one row for each unique owner_id.

It looks like this does the trick:

qs = Baz.objects.all()
qs.query.group_by = ['owner_id']

# Seems to do the trick
print [item for item in qs]
tvon
seems so close, I will look into this further.column "app_baz.X" must appear in the GROUP BY clause or be used in an aggregate function
skyl
Can you provide a code snippet? In my example, 'owner_id' is a column in the database (and if your Baz object has "owner = ForeignKey(SomeObject)" then you should have an owner_id column as well).
tvon
owner_id is a column, I getProgrammingError: column "checkins_checkin.id" must appear in the GROUP BY clause or be used in an aggregate functioncheckins_checkin.id is the primary key .. but then, if I add that to the group_by list then it moves on to having the same error for the nex field.
skyl
Is the model we're working with Checkin, or is that another fk? Also, fwiw, I'm working with Django 1.1 here.
tvon
+1  A: 

This is probably not the best solution (would like to keep it out of memory and in querysets) but:

>>> d={}
>>> [d.setdefault(str(a.owner),a) for a in qs ]
>>> d.values()

does return a list of objects, the latest for each owner. I have real reservations about the scalability of this solution.

skyl
I would suggest using this solution and then optimizing it later when it becomes a problem, maybe make a note to come back to this and fix it later.
jgeewax
Thanks, I'm using this too even though I hate it. If you do improve this code, can you please post your improvement?
slypete