I don't know how to do it... it seems simple but I was not able to do it... please give me a hint. Here is the model:
class PointOfInterest(models.Model):
name = models.CharField(max_length=100)
geom = geomodels.PointField(srid=4326)
...
class Event(models.Model):
venue = models.ForeignKey('PointOfInterest',related_name='events')
start = models.DateTimeField()
...
The event table contains many event and there could be more than event in the same PointOfInterest
. My goal is to select the first 10 events starting from today (there are obviously events in the past) near my place. For each PointOfInterest
the new list has only one entry. So I started selecting the events:
from django.db.models import Max, Min
from datetime import datetime, date, timedelta
from django.contrib.gis.measure import D
# this is my place...
pnt=Point(15.0,43.2)
# this is the date range...
start_date = datetime(date.today().year,date.today().month,date.today().day,0,0,0)
end_date = start_date + timedelta(days=30)
qs1 = Event.objects.filter(venue__geom__distance_lte=(pnt, D(km=10)))
qs2 = filter(start__range=(start_date, end_date))
qs2 = qs1.filter(start__range=(start_date, end_date))
The list now contains all the events requested but there are also more than one event for the same place... but I need only the upcoming one. The main problem is that this list could be big and my final step would be to get only the 10 upcoming events. I tried to use distinct('venue')
but then I cannot order_by('start'). So I don't know how to proceed...
I also tried to go the other way around...
qs1 = PointOfInterest.objects.filter(geom__distance_lte=(pnt, D(km=10)))
qs2 = qs1.annotate(minDate=Min('events__start'))
qs3 = qs2.filter(minDate__range=(start_date, end_date))
This list give me all the PointOfInterest
(129 in my case... while the previous list gave me 137 events... in fact there are 8 place with 2 events in the selected queries and I need to keep for each place only the upcoming one. Finally I'd like to order_by('start')
and keep only few of them).
Also in this case I tried a lot but nothing worked... anyone can help?
Thanks
EDIT
Almost there. Following the suggestion from EvilX I changed the selection but events__start__range=(start_date, end_date)
run on the places, so when I execute the 2 extra he suggested, I get the first event associated to each place (also if it's in the past). I had to change slightly the SQL query. The first attempt was this:
.extra(select={'event_pk': '
SELECT start
FROM app_event_event
AS evt
WHERE evt.venue_id=app_place_pointofinterest.id AND evt.start >= \'2014/05/04\'
ORDER BY start
LIMIT 1'})
which works... then I tried to follow the django documentation to pass a parameter to the SQL query like this:
.extra(select={'event_pk': '
SELECT start
FROM app_event_event
AS evt
WHERE evt.venue_id=app_place_pointofinterest.id AND evt.start >= %s
ORDER BY start
LIMIT 1'}, params=['2014/05/04'])
But here I get an error: StopIteration
; I tried different combination with and without the escape character but nothing worked... so I need some more advice.
Thanks