【问题标题】:Intelligent structuring of Django Models and queriesDjango 模型和查询的智能结构
【发布时间】:2015-04-15 18:32:58
【问题描述】:

我对 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 的引用。现在的问题是我们还必须重写所有查询。

这很快就会变得丑陋。假设我们想要模型上的辅助方法来获取层次结构的相关信息。例如,Country 可能有方法get_states()get_counties()get_cities()。各州将拥有get_country(),以及get_county()get_cities()。以此类推,每个人都得到了图片。这似乎是明智的,因为它将提供一个 API,系统的其他部分可以通过该 API 以与数据库布局无关的方式访问此地理信息。然而,这也意味着,由于我们编写为这些功能提供动力的查询的方式,每个模型都必须对数据库有宏观的理解。如果需要对该结构进行更改,这种未能将地理语义与我们的数据库实现分开将再次导致大量重复信息和大量工作。

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

【问题讨论】:

  • 您确定要在此处使用 OneToOne 字段吗?这意味着不仅一个城市只能在一个国家,而且一个国家只能有一个城市,这听起来不对。
  • 啊,对不起。我改变了我的例子一百万次,忘了回到 ForeignKey
  • Country 被定义了两次。
  • 不,有县和国家。抱歉,我怀疑这可能会导致一些问题:(。

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


【解决方案1】:

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

【讨论】:

  • 我想知道这种方法,但不确定它的优点。人们会做这种事情吗?当然,如果每个查询都通过模型图调用搜索,则可能会出现一些性能问题,尽管在这个阶段对我来说不是这样的问题。更大的问题是@dylrei 已经指出的:如果我使用这种自动方法,那么未来对模型结构的更改可能会导致某些歧义。
  • 不会对您的数据库进行额外查询,WHERE 中的每个查询都会有另一个条件。在我看来,这是应该自定义 QuerySet 的情况之一。
  • “在我看来,这是应该自定义 QuerySet 的情况之一。”这个问题不是经常出现吗?如果是这样,为什么这还没有内置到 Django 中?我不关心自己做子类化工作;我只是想确保我理解这些设计决策的微妙之处。谢谢!
  • 我从来没有遇到过这样的问题。如果有一些解决方案可以为您做这件事,它可能会做同样的事情:遍历您的模型结构并在外键中找到相应的字段。您还可以硬编码字段列表和它们的相应路径(仍然比每次在代码中使用完整路径更干)或制作一些元类,在类生成时生成字段和路径的静态列表。
【解决方案2】:

IMO,在这种情况下,完全标准化的解决方案是“正确的”,但对于手头的任务可能过于繁琐。我可能会考虑稍微去规范化并将 fkey 存储到我需要经常使用的关系中。例如:

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,你会想明确说明这一点。如果您添加了一个同时链接到 City 和 Country 的表,而您的应用程序由于 Django 假设 Cities 和 Country 现在的链接方式不同而崩溃了怎么办?

【讨论】:

    猜你喜欢
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    • 2012-01-07
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 2017-08-10
    • 2015-07-13
    相关资源
    最近更新 更多