我应该如何设计/构建我的数据库和ActiveRecord关联? has_many:通过?

时间:2014-01-30 03:49:02

标签: ruby-on-rails database activerecord database-design associations

使用Rails 4(和Postgres),我试图找出构建我的ActiveRecord关联和相应的数据库表/关系的最佳方式,用于饮食跟踪应用程序。

我希望我的应用程序结构如下: 我有DocumentFoodEntry个模型。每个Document都有FoodEntries个。我希望能够像document.food_entries.each ...一样迭代它们(通过典型的has_many关联很容易)。

然而,每个FoodEntries的所有Document都需要能够(可能但不一定)被细分,因为这是逻辑的自然划分除了为整个文档执行计算之外,还必须能够执行计算。例如,我使用的是document.day(1).food_entries.each ...

此外,每天应该能够以类似的方式(再次,可选地)细分为饭菜,例如document.day(1).meal(1).food_entries.each ...

最后,必须有一种方法来记录每个文档的FoodEntries,用餐和日期的用户指定顺序。大概是使用数字序列?

我在想我有几种方法可以做到这一点:

  1. 使用简单的has_many关系。 day表中包含mealsortfood_entries列,如果没有提供日/餐,daymeal的值保留为空白或给出默认值。使用基于逻辑的方法来获取和分类一天或一餐的条目。

    概要

    class Document
      has_many :food_entries
    
    class FoodEntry
      belongs_to :document
    

    潜在问题:

    • 这可能会使表中的事情变得有些混乱?
    • 细分事物的所有逻辑都必须手工编码。
    • 存储/使用用户定义(即任意)排序顺序可能会有点复杂?条目和日和餐的订单必须存储在一个序列中并从中推断(除非添加了更多列)。

  2. 使用has_many :through通过daysmeals(命名?)表格设置关联。日期/用餐不是&#39的条目; t指定获得默认值。这两个表都有自己的sort列,以及food_entries表。

    概要

    class Document
      has_many :days
      has_many :meals, through: :days
      has_many :food_entries, through: :days (AND :meals???)
    
    class Day
      belongs_to :document
      has_many :meals
      has_many :food_entries, through: :meals
    
    class Meal
      belongs_to :day
      has_many :food_entries
    
    class FoodEntry
      belongs_to :meal
    

    潜在问题:

    • 增加了不必要的关系复杂性? (考虑到几天或至少是饭菜是可选的)
    • 我是否可以在has_many :food_entries through: ...模型中使用Document,如果它必须通过两个表格?

  3. 上述两种方法之间的折衷方案:拥有days表,但在meal表格的列中保留food_entries

  4. 还有别的吗?多态关联?

  5. 这让我的头脑变得有点复杂,所以我真的很难解决我应该使用的东西。 正确处理事情的方法是什么?


    一些相关但完全可选的最终问题:

    • 理想情况下,day值可以是datetime值或任意字符串,具体取决于用户设置的内容。这可能吗?
    • 有人能指出我可以告诉我排序/订购策略的资源吗?就像我说的那样,我认为最简单的方法是使用一系列数字,但我并不完全确定如何使用这样的序列。

2 个答案:

答案 0 :(得分:1)

has_many:通过

如果您想将多个FoodEntries归因于Document,则只能使用has_many :through关系,如下所示:

#app/models/document.rb
Class Document < ActiveRecord::Base
    has_many :food_entries_types
    has_many :food_entries, through: :food_entries_types
end

#app/models/food_entry_type.rb
Class FoodEntryType < ActiveRecord::Base
    belongs_to :document
    belongs_to :food_entry
end

#app/models/food_entry.rb
Class FoodEntry < ActiveRecord::Base
    has_many :food_entries_types
    has_many :documents, through: :food_entries_types
end

这只允许您将多个food_entries与相似数量的documents相关联。虽然您可以添加特定的days&amp; meals属性为连接模型,允许您根据需要调用它们


<强>作用域

我相信更好的选择是使用ActiveRecord scopes

#app/models/document.rb
Class Document < ActiveRecord::Base
    has_many :food_entries

    #uses [wday][3]
    #needs to return dates for specific day
    scope :day, ->(day = 1) { where(created_at: Date::DAYNAMES[day]) }
    scope :meal, ->(meal = 1) { where(meal: meal) }
end

由于范围可以链接,我相信你能够做到这一点:

food = Document.day(1).meal(2).food_entries

课程方法

您还可以创建class_method来实现类似的目标:

#app/models/document.rb
Class Document < ActiveRecord::Base
    has_many :food_entries

    def self.sorted(day = 1, meal = 1)
        document = self.where("created_at = ? AND meal = ?", Date::DAYNAMES[day], meal)
    end
end

#app/controllers/documents_controller.rb
def show
    @document = Document.sorted
end

答案 1 :(得分:0)

我最终实现了我的#2选项。这给了我最大的灵活性并且运作良好。我认为这是我用例最优雅的方法。