2016-05-13 54 views
2

因此,这里是我的很好的功能:简化Django的ORM查询

def is_serviceable(address): 
    """ 
    Checks if an address can be serviced by an Employee. 
    Returns True if address' lat-lng intersects with any of Employees' coverage. 
    If it doesn't, but address' locality is listed in world District model, 
    returns False and locality name. 
    Just returns False if nothing found. 

    -- address -- django-address Address object. Address should be geocoded (i.e 
    have latitude and longitude fields correctly filled) 
    """ 
    pnt = Point(address.longitude, address.latitude) 
    employee_exists = Employee.objects.filter(coverage__mpoly__intersects=pnt).exists() 
    district = District.objects.filter(mpoly__intersects=pnt).first() 
    if (employee_exists): 
     return employee_exists 
    elif district: 
     return False, district.name 
    else: 
     return False 

得到我想要我必须执行两个数据库查询什么,首先要Employee表,然后到区表。这似乎不是一种可扩展的方法。然而,由于员工的覆盖属性实际上是与区模型许多一对多关系领域:

class Employee(models.Model): 
    coverage = models.ManyToManyField(
     District, 
     related_name="employees", 
     verbose_name=_("assigned districts") 
     ) 

class District(models.Model): 
    mpoly = models.MultiPolygonField() 

...我敢肯定,有一种方法来查询压缩到一个查询集,或者,如果Django的ORM不适合这种单一的SQL语句。不过,我不知道从哪里开始挖掘。有什么想法吗?

编辑:解决感谢Django的注释功能。

回答

1

下面是基于Sayse的答案的解决方案:

pnt = Point(address.longitude, address.latitude) 
district = District.objects.filter(mpoly__intersects=pnt).annotate(emps=Count('employees')).only('name').first() 
if district: 
    if district.emps: 
     return True 
    return False, district.name 
else: 
    return False 

还没有分析它,但它只是一个查询而不是两个,这正是我一直在寻找的。此外,它首先查询区表,这意味着员工表上的负载较轻。

1

Employee对象访问其相关区对象:

x=Employee.objects.all()[0] 
print x.district_set.all() 
1

你可以做对员工的计数的注释,然后在那

District.objects.filter(mpoly__intersects=pnt).annotate(emps=Count('employees')).filter(emps__gt=0).first() 

您也可以将数过滤想要使用.only('name'),因为这是您在该区域实际使用的所有内容。

免责声明:这是未经测试,我不知道我这实际上将是任何更快,因为exists是不是很密集

+1

谢谢!注释非常酷,正是我所需要的。 – khvn

+1

@khvn - 不用担心,尽情享受吧!你可以通过改变'elif'到'if区域'来删除检查'emp'(s)两次。 – Sayse

+1

很好的结果。再次感谢! – khvn