简化django ORM查询

时间:2016-05-13 16:50:18

标签: python django

所以这是我的好功能:

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表,然后是District表。这似乎不是一种可扩展的方法。但是,由于Employee的coverage属性实际上是区域模型的多对多关系字段:

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的注释功能。

3 个答案:

答案 0 :(得分:1)

员工对象可以访问其相关的区域对象:

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

答案 1 :(得分:1)

您可以对员工的数量进行注释,然后过滤该计数

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

您可能还想使用.only('name'),因为这是您在该区实际使用的所有内容。

免责声明:这是未经测试的,我不知道我实际上会更快,因为exists不是非常密集

答案 2 :(得分:1)

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

home.json

Haven尚未对其进行分析,但它只是一个查询而不是两个查询,这正是我一直在寻找的。此外,它首先查询区表,这意味着在Employee表上加载更轻。