2012-03-03 24 views
14

Tôi có một mô hình Order, có origin PointField và range IntegerField. Hơn nữa, có một mô hình UserProfile, trong đó có một điểm số geo_location. Bây giờ, tôi có một phiên bản User, user. Tôi muốn chọn tất cả Orders, có khoảng cách giữa Order.originuser.userprofile.geo_location ít hơn giá trị (mét) trong trường mô hình Order.range.Bộ lọc khoảng cách GeoDjango với giá trị khoảng cách được lưu trong mô hình - truy vấn

Vì vậy, một lần nữa, các mô hình đơn giản:

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

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

Tôi đã có làm việc này (đi qua các khoảng cách tĩnh):

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

My tiếp theo (không thành công) thử là sử dụng một biểu thức F() để sử dụng giá trị từ trường 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 

Tôi nghĩ rằng vấn đề là D() không chạy lười biếng - tôi có thể hiểu điều đó. Vì vậy, tôi đã cố gắng để chỉ lấy giá trị nguyên từ range lĩnh vực (số nguyên) mà tôi cho là để làm việc, nhưng:

>>> 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. 

Vậy làm thế nào tôi có thể thực hiện những gì tôi đang cố gắng? Bất kỳ trợ giúp được đánh giá cao!

Trả lời

8

Tôi nghĩ bạn sẽ phải bỏ một số SQL vào để làm những gì bạn muốn, vì người trợ giúp GeoDjango không có cách nào để bạn tạo một đối tượng Distance phù hợp đồng thời là đối tượng Django F (ví dụ: trường tra cứu). Bạn không nói bạn đang sử dụng cơ sở dữ liệu nào, nhưng dưới đây là cách bạn làm điều đó với 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] 
) 

Hãy giải thích điều gì đang xảy ra ở đây, vì nó khá gai góc.Đi từ bên trái:

  • .extra() cho phép bạn thêm các trường và giới hạn bổ sung vào truy vấn; ở đây chúng tôi đang thêm một hạn chế
  • ST_Distance() được chức năng PostGIS rằng các nhà điều hành GeoDjango distance_lte chuyển đổi thành (từ the Django documentation)
  • ST_PointFromText() chuyển đổi từ cú pháp WKT vào một đối tượng PostGIS Geometry
  • 4326the default SRID for Django, nhưng không phải cho PostGIS vì vậy chúng tôi phải chỉ định nó
  • chúng tôi phải CAST trường của bạn để tăng gấp đôi độ chính xác bởi vì bạn đang sử dụng số nguyên
  • sau đó chúng tôi phải chia cho 1000 để chuyển đổi từ mét sang kilômét, mà tôi tin là những gì chúng ta cần
  • GeoDjango Point lĩnh vực có một accessor .wkt mang đến cho đại diện WKT của họ, mà chúng tôi cần thiết trước đó trong cuộc gọi của chúng tôi ST_PointFromText()

Lưu ý rằng theo các tài liệu PostGIS, ST_Distance() không sử dụng chỉ mục, vì vậy bạn có thể muốn điều tra bằng cách sử dụng ST_DWithin() thay thế (được ghi lại ngay sau ST_Distance()).

+0

Cảm ơn bạn đã nỗ lực đưa câu trả lời này cùng nhau. Cuối cùng tôi đã kết thúc với một giải pháp được mô tả ở đây: http://blog.adamfast.com/2011/11/radius-limited-searching-with-the-orm/ –

+0

Cảm ơn câu trả lời hữu ích. Nhưng, tôi đã tìm thấy ST_Distance() để trở về độ cô đơn/lat thay vì mét. Dưới đây là những gì tôi đã làm để có được mét: nhà cung cấp = providers.extra ( trong đó = ['ST_Distance (ST_Transform (origin, 2163), ST_Transform (ST_PointFromText (% s, 4326), 2163)) <= range'], params = [user.profile.geo_location.wkt] ) SRID 2163 là phép chiếu hình nón của các quốc gia thống nhất. Tôi tìm thấy đúc để tăng gấp đôi không cần thiết quá. –

+1

Tôi tin rằng 'ST_Distance()' hoạt động khác nhau tùy thuộc vào việc bạn cung cấp cho nó các đối tượng 'Geometry' hay' Geography'. (Điều này có thể hoặc có thể không thay đổi kể từ câu trả lời ban đầu của tôi.) –

Các vấn đề liên quan