我已经在这个问题上挣扎了一段时间,研究过几十个地方,试过多种方法,但我仍然在努力寻找优雅高效的解决方案。我确信一些模特主脑可以很快让我走上正轨。
我正在尝试使用Django 1.3建模位置数据。例如,州有城市,城市有建筑物,建筑物,房间和房间都有设备/硬件。我有一个有效的解决方案,但我遇到了遍历关系的(n + 1)个查询问题。
在某些视图中,我想指定一个州,然后逐步增加州,建筑,房间和设备中的所有城市。随着建筑物,房间和设备的数量,查询数量迅速增加。目前,如果我想查看州内的所有建筑物,房间,设备,我会对该州的所有城市执行查询,然后使用_set方法向后关注foriegn键,循环城市,循环建筑物,循环房间等。
models.py
class City(models.Model):
city = models.CharField(max_length=128)
city_code = models.CharField(max_length=6)
state = USStateField(_('state'))
class Address(models.Model):
address1 = models.CharField(max_length=128, blank=True)
address2 = models.CharField(max_length=128, blank=True)
city = models.ForeignKey(City)
zipcode= models.CharField(max_length=5, blank=True)
zip_plus4=models.CharField(max_length=4, blank=True)
class Building(Address, CommonModel):
building = models.CharField(max_length=128, help_text='Building Name')
building_code = models.CharField(max_length=2 )
class Roomcode(models.Model):
roomcode = models.CharField(max_length=2, \
roomcode_name = models.CharField(max_length=64, help_text='Description or name for code')
class Room(models.Model):
building = models.ForeignKey(Building)
name = models.CharField(max_length=128)
roomcode = models.ForeignKey(Roomcode)
class Device(models.Model):
name = models.CharField(max_length=64, unique=True)
description = models.CharField(max_length=64, blank=True)
room = models.ForeignKey(Room)
目前要获取所有城市,建筑,房间,设备的状态,我会做以下事情,
views.py
@render_to('place/index.html')
def index(request):
sites = City.objects.distinct().order_by('state')
return { 'sites': sites, }
index.html,类似......
{% for site in sites %}
{% for building in site.building_set.all %}
print building details
{% for room in building.room_set.all %}
print room details
{% for device in room.device_set.all %}
print device details
很多循环和大量查询!!
走向另一个方向很简单,如果我想获取所有设备,然后获取他们可以使用select_related查询的位置信息
devices = Device.objects.select_related().all()
问题:
我想要解决的主要问题是sql查询的线性增加与数据库中的项目数量的关系。例如,我目前在数据库中有少量设备,32个设备,大约20个城市,30个建筑物和35个房间。使用select_related查询所有设备仅使用4个查询,并且需要大约300 ms的sql时间,因为它可以跟随外键。
走向另一个方向,显示按州订购的所有州,城市,建筑物,房间和设备需要76个查询并且需要3231毫秒sql时间。添加一个房间,添加一个查询,几十个房间中的几个设备将使我的页面静止不动。
select_related跟随外键,如果我需要知道设备所在的城市/建筑物/房间,则效果很好。
device = Device.objects.get(id=1)
room = device.room # hits database
buidling = device.room.building # hits database again.
device = Device.objects.select_related().get(id=1)
room = device.room # doesn't hit
building = device.building # doesn't hit
问题发生在另一个方向,如我上面的index.html示例中所述,在检索城市之后,我必须遍历每个相应的建筑物,房间和设备以查看所有设备,点击每个城市的数据库,建筑,房间,设备。基准测试显示sql查询的数量很快失控,并大大增加了页面加载时间。
答案 0 :(得分:1)
由于您的对象非常分层,因此MPTT和通用关系没有意义。例如,您不会将建筑物放入房间。这些关系中的一些看起来是永久的,有些不是,即房间永远不会离开建筑物,但设备可能。我没有看到您当前的建模方案有问题。
由于您关注查询的数量,请查看查询集的select_related
功能。这使您可以在单个SQL调用中获得深层外部关系,并在请求周期的持续时间内缓存它们。这可以减轻数据库事务的重量。
但是,简单的问题是:您是否对查询数量进行了基准测试?这对您的应用程序的性能是否真的致命?
另一方面,你在一个真正描述的集合中给很多个性留下了深刻的印象:[name,description,parent]。如果您重新编写类以反映该结构,则MPTT的子类可以正确处理关系(例如,永远不要让建筑物在房间中),并且您可以在一个树中对整个事物进行建模。您可以轻松设计通用的“详细信息和属性”模型,然后包含城市和地址等所需的额外项目。