2012-03-03 40 views
14

Mam model Order, który ma origin PointField i range IntegerField. Ponadto istnieje model UserProfile, który ma PointField geo_location. Teraz mam instancję User, user. Chcę wybrać wszystkie Orders, których odległość między Order.origin i user.userprofile.geo_location jest mniejsza niż wartość (metry) w polu modelu Order.range.Filtr odległości GeoDjango z wartością odległości zapisaną w modelu - zapytanie

więc ponownie, uproszczone modele:

class Order(models.Model): 
    origin = models.PointField() 
    range = models.IntegerField(blank=True, default=10000) 

class UserProfile(models.Model): 
    geo_location = models.PointField() 

ja mam tej pracy (przechodząc dystans statycznie):

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, D(m=3000))) 

My next (nieudany) Spróbuj było użyć F() wyraz użyj wartości z pola Order.range:

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, D(m=F('range')))) 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/measure.py", line 165, in __init__ 
self.m, self._default_unit = self.default_units(kwargs) 
    File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/measure.py", line 49, in default_units 
if not isinstance(value, float): value = float(value) 
TypeError: float() argument must be a string or a number 

Myślę, że problem polega na tym, że D() nie działa leniwie - mogę to zrozumieć. Więc starałem się po prostu wziąć wartość surowca z pola range (Integer), które mam do pracy, ale:

>>> Order.objects.filter(origin__distance_lte=(user.profile.geo_location, F('range'))) 
Traceback (most recent call last): 
File "<console>", line 1, in <module> 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 69, in __repr__ 
data = list(self[:REPR_OUTPUT_SIZE + 1]) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 84, in __len__ 
self._result_cache.extend(self._iter) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/query.py", line 273, in iterator 
for row in compiler.results_iter(): 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 680, in results_iter 
for rows in self.execute_sql(MULTI): 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 725, in execute_sql 
sql, params = self.as_sql() 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 68, in as_sql 
where, w_params = self.query.where.as_sql(qn=qn, connection=self.connection) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/where.py", line 92, in as_sql 
sql, params = child.as_sql(qn=qn, connection=connection) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/db/models/sql/where.py", line 95, in as_sql 
sql, params = self.make_atom(child, qn, connection) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/db/models/sql/where.py", line 47, in make_atom 
spatial_sql = connection.ops.spatial_lookup_sql(data, lookup_type, params_or_value, lvalue.field, qn) 
File "/Users/danger/devel/.virtualenvs/proj/lib/python2.7/site-packages/django/contrib/gis/db/backends/postgis/operations.py", line 531, in spatial_lookup_sql 
raise ValueError('Argument type should be %s, got %s instead.' % (arg_type, type(value[1]))) 
ValueError: Argument type should be (<class 'decimal.Decimal'>, <class 'django.contrib.gis.measure.Distance'>, <type 'float'>, <type 'int'>, <type 'long'>), got <class 'django.db.models.expressions.F'> instead. 

Więc jak mogę osiągnąć to, co usiłuję? Każda pomoc jest doceniana!

Odpowiedz

8

Chyba będziesz musiał upuścić trochę SQL w celu zrobienia tego, co chcesz, jak pomocnicy GeoDjango nie mają sposób robisz odpowiednią Distance obiekt, który jest jednocześnie Django F obiekt (czyli pole lookup). Nie mów, której bazy danych używasz, ale oto jak to zrobić z PostGIS:

Order.objects.all().extra(
    where=['ST_Distance(origin, ST_PointFromText(%s, 4326)) <= CAST(range AS double precision)/1000'], 
    params=[user.profile.geo_location.wkt] 
) 

Wyjaśnijmy, co dzieje się tutaj, ponieważ jest to bardzo drażliwy.Idąc od lewej:

  • .extra() pozwala na dodanie dodatkowych pól i ograniczeń do zapytania; tutaj dodajemy ograniczenie
  • ST_Distance() jest funkcja PostGIS że operator GeoDjango distance_lte przekształca się (od the Django documentation)
  • ST_PointFromText() konwertuje od składni WKT do obiektu PostGIS geometrii
  • 4326 jest the default SRID for Django, ale nie dla PostGIS więc musimy określić ją
  • musimy CAST swoje pole do podwójnej precyzji, ponieważ używasz całkowitą
  • to musimy podzielić przez 1000 przekonwertować z liczników w kilometrach, które uwierzyć jest to, czego potrzebujemy
  • GeoDjango Point pola mają akcesor .wkt co daje ich reprezentacji WKT, co nam potrzebne wcześniej w naszej ST_PointFromText() rozmowy

Należy zauważyć, że zgodnie z dokumentacją PostGIS, ST_Distance() nie korzysta indeksy, więc możesz chcieć zbadać, używając zamiast tego ST_DWithin() (jest to udokumentowane zaraz po ST_Distance()).

+0

Dziękujemy za wysiłek włożenia tej odpowiedzi w całość. W końcu znalazłem rozwiązanie opisane tutaj: http://blog.adamfast.com/2011/11/radius-limited-searching-with-the-orm/ –

+0

Dzięki za pomocną odpowiedź. Ale znalazłem ST_Distance() zwracania lon/lat stopni zamiast metrów. Oto, co zrobiłem, aby uzyskać liczniki: providers = providers.extra ( gdzie = ['ST_Distance (ST_Transform (początek, 2163), ST_Transform (ST_PointFromText (% s, 4326), 2163)) <= zakres'], params = [user.profile.geo_location.wkt] ) SRID 2163 to stożkowa projekcja stanów zjednoczonych. Zauważyłem, że rzucanie castingu jest niepotrzebne. –

+1

Wierzę, że 'ST_Distance()' działa inaczej w zależności od tego, czy nadajesz obiektom 'Geometry' lub' Geography'. (To może lub nie uległo zmianie od czasu mojej oryginalnej odpowiedzi.) –

Powiązane problemy