Django模型和查询的智能结构

时间:2015-02-14 22:59:31

标签: python django django-models orm django-orm

我对Django的数据库抽象层相对较新。说实话,我没有那么多通过任何系统与数据库交互的经验。无论如何,我正在从事个人项目,对我目前的设计不满意,至少就我的疑问而言。我想我会向社区寻求关于如何处理我的问题的建议。

以下是开始讨论的简化案例。假设我们正在建模一个国家的信息。我们可能会提出以下内容

class Country(models.Model):
  ...

class Region(models.Model):
  country = models.ForeignKey(Country)
  ...

class State(models.Model):
  region = models.ForeignKey(Region)
  ...

class County(models.Model):
  state = models.ForeignKey(State)
  ...

class City(models.Model):
  state = models.ForeignKey(County)
  ...

好。我很满意这种结构,因为没有多余的信息。好极了。但是,当我想要制作许多我想做的查询时,我得到这样的陈述:

# get the country a city is in
def get_country(city):
  return Country.objects.get(region__state__county__city=city)

这很有效,但由于以下原因让我感到困扰:当我指定模型时,我给了Djano数据库的结构。也就是说,系统已经知道一个城市在一个县,一个县在一个州,一个州在一个地区,最后一个地区在一个国家。当我写上面的查询时,我提供冗余信息;结构已经暗示每个城市只有一个国家。因此,鉴于一个城市,它显然处于哪个国家。为什么我必须再次指定它?

由于多种原因,必须这样做会对开发产生负面影响。首先考虑,例如,a get_region()函数:

#get the region a city is in:
def get_region(city):
return Region.objects.get(state__county__city=city)

关于这个的一切几乎与get_country函数相同; get_country查询已经包含了获取该区域所需的信息,因此我再次通知系统城市位于州内,这些县位于区域内。

此外,请考虑以下情况:我意识到我误解了原始要求,我甚至不需要考虑“区域”。因此,擦除该模型并更改State模型以包含对Country的引用。问题是现在我们还必须重写所有查询。

这快速变得丑陋。假设我们希望在模型上使用辅助方法来获取层次结构上的相关信息。例如,国家/地区可能包含方法get_states()get_counties()get_cities()。各州将get_country(),以及get_county()get_cities()。等等,每个人都能了解情况。这似乎是明智的,因为它将提供一个API,系统的其他部分可以通过该API以与数据库布局无关的方式访问此地理信息。但是,这也意味着,由于我们编写驱动这些函数的查询的方式,每个模型必须对数据库有宏观的理解。如果需要对该结构进行更改,那么将地理语义与数据库实现分离的失败将再次导致大量重复信息和大量工作。

所以,朋友们,请赐教。我没有正确思考模型之间的关系吗?我不欣赏Django数据库层的哲学吗?我是否遗漏了一些可以帮助我澄清此功能的功能?

2 个答案:

答案 0 :(得分:0)

国际海事组织,这是一个完全正常化的解决方案是"正确"但对于手头的任务来说可能太麻烦了。我可能会考虑稍微进行非规范化并将fkeys存储到我需要经常使用的关系中。例如:

class City(models.Model):
    county = models.ForeignKey(County)
    region = models.ForeignKey(Region)
    state = models.ForeignKey(State)
    country = models.ForeignKey(Country)

    def save(self, *args, **kw):
        self.region = self.county.region
        self.state = self.county.region.state
        self.country = self.county.region.state.country
        super(City, self).save(*args, **kw)

这样做是错误的"解决方案,但可能适合您的需求,特别是如果您重视数据库效率低于开发简单性。

无论如何,我会断言你希望Django假设City应该如何链接回Country,你想明确说明。如果您添加了一个链接到城市和国家/地区的表格,并且您的应用程序崩溃了怎么办?因为Django认为城市和国家现在的链接方式不同?

答案 1 :(得分:0)

考虑创建自己的QuerySet子类,当模型中没有这样的字段时,它将在外键中查找字段,并在模型中将此QuerySet用作管理器。