【问题标题】:Refactoring composite primary key to simple key for Rails?将复合主键重构为 Rails 的简单键?
【发布时间】:2017-04-29 04:39:35
【问题描述】:

我的应用程序使用 Ruby on Rails ActiveRecord 模型,如果未安装第三方 gem,例如 composite-primary-keys,则不允许使用复合键。有没有办法可以将复合键重构为简单键,使其适合这种范式,或者我应该硬着头皮安装 gem?

我仍处于早期设计阶段,所以我没有需要担心的数据,我希望尽可能忠实于 Rails 惯用语。

我正在创建一个食谱数据库,可以逐步列出成分和说明。数据库架构类似于下图,在 Recipe_Steps 和 Recipe_Step_Ingredients 表中使用复合键(图像底部中心)。

【问题讨论】:

    标签: sql ruby-on-rails database database-design refactoring


    【解决方案1】:

    您很可能会过度思考并使问题过于复杂化。坚持使用名为 id 的单个主键的 AR 习惯用法。

    对于连接表,使用外键而不是复合 PK。还要遵守命名约定,除非您想通过违反最小意外原则来显得无能或惹恼其他开发人员。这意味着:

    • 对所有内容(表名、列、索引名等)使用snake_case
    • 不要在列前加上表名。它只是使应用程序中的每个变量更长,并且在 ORM 中不需要。
    • _id 用于外键列。前任; parent_id
    • 使用_at 作为时间戳。前任; confirmed_at
    • 除非有更具描述性的名称,否则请使用 thing_other_things 连接表

    这些情况中的许多应该只使用间接关系来加入层次结构,而不是复制外键。

    这是一个示例数据库架构:

    ActiveRecord::Schema.define(version: 20161214013752) do
    
      # These are extensions that must be enabled in order to support this database
      enable_extension "plpgsql"
    
      create_table "ingredient_types", force: :cascade do |t|
        t.string   "name"
        t.string   "description"
        t.datetime "created_at",  null: false
        t.datetime "updated_at",  null: false
      end
    
      create_table "ingredients", force: :cascade do |t|
        t.integer  "ingredient_type_id"
        t.datetime "created_at",         null: false
        t.datetime "updated_at",         null: false
        t.index ["ingredient_type_id"], name: "index_ingredients_on_ingredient_type_id", using: :btree
      end
    
      create_table "recipe_ingredients", force: :cascade do |t|
        t.integer  "recipe_id"
        t.integer  "ingredient_id"
        t.float    "quantity"
        t.datetime "created_at",    null: false
        t.datetime "updated_at",    null: false
        t.index ["ingredient_id"], name: "index_recipe_ingredients_on_ingredient_id", using: :btree
        t.index ["recipe_id"], name: "index_recipe_ingredients_on_recipe_id", using: :btree
      end
    
      create_table "steps", force: :cascade do |t|
        t.integer  "recipe_id"
        t.integer  "ordinal"
        t.text     "instruction"
        t.datetime "created_at",   null: false
        t.datetime "updated_at",   null: false
        t.index ["recipe_id"], name: "index_steps_on_recipe_id", using: :btree
      end
    
      create_table "recipes", force: :cascade do |t|
        t.string   "name"
        t.string   "description"
        t.datetime "created_at",  null: false
        t.datetime "updated_at",  null: false
      end
    
      add_foreign_key "ingredients", "ingredient_types"
      add_foreign_key "recipe_ingredients", "ingredients"
      add_foreign_key "recipe_ingredients", "recipes"
      add_foreign_key "steps", "recipes"
    end
    

    class IngredientType < ApplicationRecord
      has_many :ingredients
    end
    
    class Ingredient < ApplicationRecord
      belongs_to :ingredient_type
      has_many :recipe_ingredients
      has_many :recipes, through: :recipe_ingredients
    end
    
    class RecipeIngredient < ApplicationRecord
      belongs_to :recipe
      belongs_to :ingredient
      has_one :ingredient_type, through: :ingredient
    end
    
    class Step < ApplicationRecord
      belongs_to :recipe
    end
    
    class Recipe < ApplicationRecord
      has_many :recipe_ingredients
      has_many :ingredients, through: :recipe_ingredients
      has_many :steps
    end
    

    【讨论】:

    • 这不是我的确切架构,我只是将其用作参考。但这些都是非常好的提示。我也不是 SQL 数据库设计专家……您如何建议在不使用复合键的情况下对 recipe_step_ingredients 建模?一个食谱有很多步骤,每个步骤可以有很多成分,所以我很难找出一个不涉及复合键的简单模式。
    • 它本身可能只是过于复杂了。您是否真的需要在步骤中存储成分的数量,而不仅仅是在连接recipesingredients 的连接表上?
    • 大多数食谱都将成分列在一起,然后您只需在步骤中写下mix half the four with the eggs 之类的内容。亲吻。
    • 如果我在成分表中列出了数量,那么您的问题是每个成分/数量组合都有不同的行,例如1汤匙番茄酱,2汤匙。番茄酱等
    • 但是你为什么要那个而不是总数呢?如果你想在份数之后再做规模食谱之类的事情,那会变得非常混乱。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-23
    • 2016-07-04
    • 2011-02-09
    • 2013-12-19
    • 1970-01-01
    • 2014-08-24
    • 2011-09-21
    相关资源
    最近更新 更多