2016-05-13 62 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