【问题标题】:Django: Two models with OneToOneField vs a single modelDjango:具有 OneToOneField 的两个模型与单个模型
【发布时间】:2015-12-25 13:54:15
【问题描述】:

让我们想象一个我有一个简单的模型配方:

class Recipe(models.Model):
    name = models.CharField(max_length=constants.NAME_MAX_LENGTH)
    preparation_time = models.DurationField()
    thumbnail = models.ImageField(default=constants.RECIPE_DEFAULT_THUMBNAIL, upload_to=constants.RECIPE_CUSTOM_THUMBNAIL_LOCATION)
    ingredients = models.TextField()
    description = models.TextField()

我想创建一个视图,列出所有可用的食谱,其中仅使用名称、缩略图、preparation_time 和描述的前 100 个字符。此外,我将有一个专用视图来呈现单个配方的所有剩余细节。

从效率的角度来看,由于描述可能是一个长文本,将额外信息存储在单独的模型中是否有意义,比如说“RecipeDetails”,它不会在列表视图中提取,而只能在详细视图(可能使用 prefetch_related 方法)?我正在考虑一些事情:

class Recipe(models.Model):
    name = models.CharField(max_length=constants.NAME_MAX_LENGTH)
    preparation_time = models.DurationField()
    thumbnail = models.ImageField(default=constants.DEFAULT_THUMBNAIL, upload_to=constants.CUSTOM_THUMBNAIL_LOCATION)
    description_preview = models.CharField(max_length=100)

class RecipeDetails(models.Model):
    recipe = models.OneToOneField(Recipe, related_name="details", primary_key=True)        
    ingredients = models.TextField()
    description = models.TextField()

在我最近的在线搜索中,人们似乎建议 OneToOneField 仅用于两个目的:1. 继承和 2. 扩展现有模型。在其他情况下,应将两个模型合并为一个。这可能表明我在这里遗漏了一些东西。这是对 OneToOneField 的合理使用,还是只会增加整体设计的复杂性?

【问题讨论】:

    标签: django database-design django-models


    【解决方案1】:

    继承

    不要那样做,因为继承只有在你有基类/子类关系时才有用。经典的例子是动物和猫/狗,其中猫/狗都有一些可以提取的基本属性,但您的RecipeRecipeDetail 没有。

    从效率的角度来看,由于描述可能很长 文本,将额外信息存储在单独的文件中是否有意义 型号

    将额外信息存储在单独的模型中不会提高任何效率。下划线数据库将创建类似于ForeignKey 字段和加上unique=True 以确保唯一性。就我而言,OneToOneField 仅在您的原始模型难以更改时有用,例如,它来自第三方包或其他一些尴尬的情况。否则我仍然考虑将它们添加到Recipe 模型中。在这种情况下,您可以轻松管理您的模型,同时避免进行一些额外的查找,例如 recipe.recipedetail.description,您只需执行 recipe.description

    【讨论】:

    • 一些不相关的东西,你的模型名称应该总是使用单数形式而不是复数形式,django 会在需要时添加复数形式。
    • 这是否意味着代码“for r in Recipe.objects.all(): print r.name”无论 description 是 OneToOneField 还是(可能很长的)TextField 都将具有相同的性能?出于这个问题的目的,我将 OneToOneField 和 ForeignKey 互换。
    • 我不知道您从哪里得到有关性能问题的想法,但访问字段 name 与其他字段的外观无关。我的建议只是从纯代码结构的角度来看,我是说通过将细节添加为OneToOne 字段不会有任何收获,所以只需将它们全部放在Recipe 模型中即可。具有许多字段的模型不会使其更复杂,也不会降低性能。
    • 是的,我更关心“Recipe.objects.all()”语句,如果我将描述直接存储在 Recipe 类中,该语句(可能是错误的)将评估为一组更大的对象。特别是,如果我不使用描述字段,如果我只想列出它们,阅读所有食谱的所有描述似乎会产生很大的开销。
    • TextField 真的一点都不可怕,除非你有数百万条记录。此外,django 已经拥有满足您需求的工具。你可以做Recipe.objects.values('name', 'preparation_time'),它会在后台做SELECT name, preparation_time FROM Recipe,所以你的文本字段不会在缓存中。docs.djangoproject.com/en/1.9/ref/models/querysets/…
    【解决方案2】:

    不,拆分食谱是不合理的。首先,您的模型应该包含作为“配方”的所有属性(没有成分的配方根本不是配方)。其次,如果您想提高性能,请使用Django's Cache Framework(它是专门为提高性能问题而创建的)。第三,保持简单,不要过度设计你的开发周期。您现在真的需要提高性能吗?

    希望对你有帮助!

    【讨论】:

      【解决方案3】:

      开发中的第一个错误,您在第一个版本运行之前就考虑效率。

      现在尝试拥有一个可以运行的第一个版本,稍后您可以根据第一个版本的用例来考虑更快。在此之后,您可以检查模型和关系,或者只有模型中的新字段或使用 Django Cache 的视图可以完成这项工作。

      您首先考虑的效率是“反规范化”您的数据库顺便说一句,当模型中的完整描述更新完成后,您需要使用“描述预览”字段对模型启动一次更新。在数据库级别触发?用于在应用程序级别更新的python代码?代码设计中的噩梦......在代码运行之前。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-26
        • 1970-01-01
        • 2014-12-24
        • 2017-12-20
        • 1970-01-01
        • 1970-01-01
        • 2013-12-01
        • 2016-11-06
        相关资源
        最近更新 更多