简体   繁体   English

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

[英]How should I design/structure my database and ActiveRecord associations? has_many :through?

Using Rails 4 (and Postgres), I'm trying to work out the best way in which to structure my ActiveRecord associations and corresponding database tables/relationships for a diet tracking app. 我正在尝试使用Rails 4(和Postgres)来为饮食跟踪应用程序构造ActiveRecord关联和对应的数据库表/关系的最佳方法。

I want my app to be structured as follows: I have Document and FoodEntry models. 我希望我的应用程序的结构如下:我有DocumentFoodEntry模型。 Each Document has a number of FoodEntries . 每个Document都有许多FoodEntries I want to be able to iterate over them like document.food_entries.each ... (which is easy with your typical has_many association). 我希望能够像document.food_entries.each ...那样遍历它们(使用典型的has_many关联很容易)。

However all the FoodEntries for each Document need to be able (potentially but not necessarily) to be subdivided by day , as this is a natural division for which logic and calculations must be able to be performed, in addition to doing them for the whole document. 但是,每个Document所有FoodEntries需要(可能但不一定)按细分,因为这是自然的划分,除了对整个文档进行逻辑和计算外,还必须能够执行逻辑和计算。 For instance I'd be using something like document.day(1).food_entries.each ... . 例如,我将使用诸如document.day(1).food_entries.each ...

Furthermore, each day should be able to be subdivided (again, optionally) into meals in a similar manner, eg document.day(1).meal(1).food_entries.each ... 此外,每天应该能够以类似的方式细分(再次,可选地) 进餐 ,例如document.day(1).meal(1).food_entries.each ...

Lastly, there must be a way to record the user-specified order that the FoodEntries , meals, and days are in for each document. 最后,必须有一种方法来记录用户指定的每个文档的FoodEntries ,进餐和天数的订单。 Presumably using number sequence(s)? 大概使用数字序列?

I was thinking there are a few ways I could do this: 我当时在想有几种方法可以做到这一点:

  1. Use a simple has_many relationship. 使用简单的has_many关系。 Have day , meal and sort columns in the food_entries table, where the value for day and meal is left blank or given a default value if a day/meal isn't provided. food_entries表中有daymealsort列,其中day meal值保留为空白,或者如果未提供日/餐,则将其设置为默认值。 Use a logic-based approach to get and sort the entries for a day or meal. 使用基于逻辑的方法来获取和分类一天或一餐的条目。

    Outline: 大纲:

     class Document has_many :food_entries class FoodEntry belongs_to :document 

    Potential issues: 潜在问题:

    • This might leave things a bit messy in general in the table? 这可能会使表中的内容有些混乱?
    • All the logic for subdividing things would have to be hand-coded. 细分事物的所有逻辑都必须经过手工编码。
    • Storing/using the user-defined (ie arbitrary) sort order might get a bit complicated? 存储/使用用户定义的(即任意的)排序顺序可能会变得有些复杂? The order for entries AND days AND meals would have to stored in and inferred from one sequence (unless more columns were added). 输入项,日期和进餐的顺序必须存储在一个序列中,并可以从一个序列中推断出来(除非添加了更多列)。

  2. Use has_many :through to set up associations through days and meals (naming?) tables. 使用has_many :through通过daysmeals (命名?)表建立关联。 Entries where a day/meal isn't specified get given a default. 未指定日期/餐点的条目将设置为默认值。 Both these tables have their own individual sort column, along with the food_entries table. 这两个表都有自己的单独的sort列以及food_entries表。

    Outline: 大纲:

     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 

    Potential issues: 潜在问题:

    • Adds unnecessary relational complexity? 是否增加了不必要的关系复杂性? (consider that days or at the very least meals are meant to be optional) (考虑几天或至少要用餐是可选的)
    • Can I even use has_many :food_entries through: ... in my Document model if it would have to go through both tables? 如果必须通过两个表,我has_many :food_entries through: ...Document模型中使用has_many :food_entries through: ...吗?

  3. A compromise between the two approaches above: have a days table but keep meal in a column in the food_entries table. 上面两种方法之间的折衷:有一个days表,但将meal放在food_entries表的一列中。

  4. Something else? 还有吗 Polymorphic association(s)? 多态关联?

This is getting a bit complicated to wrap my head around, and so I'm really having a hard time working out what I should use. 我的头变得有点复杂,所以我真的很难确定应该使用什么。 What is the correct way to go about things? 做事情的正确方法是什么?


A couple of final questions which are related but completely optional: 几个相关的但完全可选的最终问题:

  • Ideally the day value could be either a datetime value or an arbitrary string, depending on what the user sets. 理想情况下, day值可以是datetime值或任意字符串,具体取决于用户设置的内容。 Is this possible? 这可能吗?
  • Could anyone point me to a resource that can inform me about sorting/ordering strategies? 谁能指出我可以参考有关排序/排序策略的资源? Like I said I assume the simplest way is to use a sequence of numbers, but I'm not exactly sure how I would work with such a sequence. 就像我说的那样,我认为最简单的方法是使用数字序列,但是我不确定如何处理这样的序列。

has_many :through has_many:通过

You'd only use a has_many :through relationship if you wanted to attribute multiple FoodEntries to Document , like this: 如果要将多个FoodEntriesDocument ,则只使用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

This would only allow you to associate many food_entries with a similar number of documents . 这样只会使您将许多food_entries与数量相似的documents相关联。 Although you could add specific days & meals attributes to the join model, allowing you to call them as required 尽管您可以将特定的daysmeals属性添加到联接模型中,但可以根据需要调用它们


Scopes 领域

I believe a much better option for you is to use ActiveRecord scopes : 我相信对您来说更好的选择是使用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

Because scopes can be chained, I believe you'd be able to do this: 由于范围可以链接在一起,因此我相信您可以做到这一点:

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

Class Method 类方法

You could also create a class_method to achieve something similar: 您也可以创建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

I ended up implementing my #2 option. 我最终实现了我的#2选项。 This has given me the most flexibility and worked out well. 这给了我最大的灵活性,并且效果很好。 I think it has been the most elegant approach for my use case. 我认为这对于我的用例来说是最优雅的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM