建模分层位置数据

时间:2011-07-07 20:59:56

标签: django django-models

我已经在这个问题上挣扎了一段时间,研究过几十个地方,试过多种方法,但我仍然在努力寻找优雅高效的解决方案。我确信一些模特主脑可以很快让我走上正轨。

我正在尝试使用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()

问题:

  1. 如何更好地建模此数据呢?
  2. 如何通过(n + 1)个查询来有效地遍历关系?
  3. MPTT - 树木?
  4. 通用关系?
  5. 原始SQL?
  6. 别的什么?
  7. 更新

    我想要解决的主要问题是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查询的数量很快失控,并大大增加了页面加载时间。

1 个答案:

答案 0 :(得分:1)

由于您的对象非常分层,因此MPTT和通用关系没有意义。例如,您不会将建筑物放入房间。这些关系中的一些看起来是永久的,有些不是,即房间永远不会离开建筑物,但设备可能。我没有看到您当前的建模方案有问题。

由于您关注查询的数量,请查看查询集的select_related功能。这使您可以在单个SQL调用中获得深层外部关系,并在请求周期的持续时间内缓存它们。这可以减轻数据库事务的重量。

但是,简单的问题是:您是否对查询数量进行了基准测试?这对您的应用程序的性能是否真的致命?

另一方面,你在一个真正描述的集合中给很多个性留下了深刻的印象:[name,description,parent]。如果您重新编写类以反映该结构,则MPTT的子类可以正确处理关系(例如,永远不要让建筑物在房间中),并且您可以在一个树中对整个事物进行建模。您可以轻松设计通用的“详细信息和属性”模型,然后包含城市和地址等所需的额外项目。