Django过滤自定义聚合函数的注释

时间:2013-12-15 20:46:37

标签: python mysql django aggregates

我正在尝试查找给定邮政编码的特定距离内的所有邮政编码。我这样做是通过使用自定义聚合函数计算距离,使用距离注释查询集,并根据“距离”字段过滤查询集。

聚合函数正确计算距离,注释正确地在查询集中创建“距离”字段。但是,筛选器始终返回空的查询集。当我使用其他字段(如'zipcode'或'state')进行过滤时,它可以正常工作,但当我使用带注释的'distance'值作为过滤器值时返回空。我做错了什么?

这是自定义聚合函数:

from django.db import models
from django.db.models import Aggregate
from django.db.models.sql.aggregates import Aggregate as AggregateImpl

class DistanceFromImpl(AggregateImpl): 
sql_function = ''
is_computed = True
is_ordinal = True

sql_template = ('3959 * acos( cos( radians(%(t_lat)f) ) * cos( radians( latitude ) ) * '
                'cos( radians( longitude ) - radians(%(t_lon)f) ) + sin( radians(%(t_lat)f) ) * '
                'sin( radians( latitude ) ) )')

def __init__(self, col, target, **extra): 
    self.col = col
    self.target = target
    self.extra = extra 

def _default_alias(self): 
    return '%s__%s' % (str(self.target), self.__class__.__name__.lower()) 

default_alias = property(_default_alias) 

def add_to_query(self, query, alias, col, source, is_summary): 
    super(DistanceFrom, self).__init__(col, source, is_summary, **self.extra) 
    query.aggregate_select[alias] = self

def as_sql(self, qn, connection):
    "Return the aggregate, rendered as SQL."

    return self.sql_template % { 't_lon': self.target.longitude,
                                 't_lat': self.target.latitude }


class DistanceFrom(Aggregate):
name="DistanceFromImpl"

def add_to_query(self, query, alias, col, source, is_summary):
    aggregate = DistanceFromImpl(col, source=source, is_summary=is_summary, **self.extra)
    query.aggregates[alias] = aggregate

我从这里抓住了这个和其他代码: https://github.com/elfsternberg/django-zipdistance/blob/master/zipdistance/models.py

我的邮政编码模型称为ZipDistance。我可以很容易地从给定的ZipDistance获得带有注释距离的查询集。所以这很好用:

>>> zip1 = ZipDistance.objects.get(zipcode='01234')
>>> qs = ZipDistance.objects.annotate(distance=DistanceFrom('zipcode', target=zip1))
>>> qs[1].distance
5  # Second entry in queryset is 5 miles away from zipcode '01234'

但按距离过滤总是返回空白:

>>> qs.filter(distance__lte=99999)
[]

我正在使用自己的fixture来填充我的数据库(这是MySQL)。问题可能是我使用的是Django 1.5版,而代码是为早期版本编写的。我只是不确定,我已经尝试了几天我能想到的一切。

1 个答案:

答案 0 :(得分:0)

仍不确定导致此错误的原因,但我通过在方法中编写自己的SQL代码来解决这个问题:

from django.db import models, connection, transaction
from math import sin, cos, radians, acos

class ZipDistance(models.Model):
    zipcode = models.CharField(max_length=5, unique=True)
    state_short = models.CharField(max_length=2)
    latitude = models.FloatField()
    longitude = models.FloatField()
    province = models.CharField(max_length=50)
    state_long = models.CharField(max_length=20)

    def get_zips_within(self, dist):
        cursor = connection.cursor()
        cursor.execute("""SELECT id, (
                          3959 * acos( cos( radians(%s) ) * cos( radians( latitude ) ) *
                          cos( radians( longitude ) - radians(%s) ) + sin( radians(%s) ) *
                          sin( radians( latitude ) ) ) )
                          AS distance FROM myapp_zipdistance
                          HAVING distance <= %s""",
                       [self.latitude, self.longitude, self.latitude, dist])
        ids = [row[0] for row in cursor.fetchall()]
        return ZipDistance.objects.filter(id__in=ids)

现在,我需要做的就是获取一定距离内的邮政编码列表如下:

>>> zip1 = ZipDistance.objects.get(zipcode='20001')
>>> zip_list = zip1.get_zips_within(dist=100)

这为我提供了华盛顿特区100英里内数据库中所有邮政编码的列表(邮政编码20001)