Reputation: 618
I've got a data model represented by the following image:
Basically, the idea is that there are many different Collections. All the Items in a single collection will have the same Attributes, but the list of attributes will be different per collection. The value of each Attribute per item will be stored in Item Attribute Values.
I'm trying to build a single page where a user can populate the attributes for an item. I'm assuming a nested form is the way to go but I'm at a loss as to how to represent this in the controller and on the page, considering the names of the attributes are in one table and the values in another.
If anyone has encountered or had to deal with a similar situation, any help would be appreciated.
Thanks
Upvotes: 1
Views: 50
Reputation: 102368
Here is one potential solution.
class Collection
has_and_belongs_to_many :items
has_and_belongs_to_many :attributes
end
class Item
has_and_belongs_to_many :collections
has_many :item_attributes
has_many :attributes, though: :item_attributes
end
class Attributes
has_and_belongs_to_many :collections
has_many :item_attributes
has_many :items, though: :item_attributes
end
class ItemAttribute
belongs_to :item
belongs_to :attribute
end
So lets look at the database layout to back these models:
ActiveRecord::Schema.define(version: 20151027173337) do
create_table "attributes_collections", id: false, force: :cascade do |t|
t.integer "attribute_id", null: false
t.integer "collection_id", null: false
end
create_table "collections", force: :cascade do |t|
t.string "title"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "collections", ["user_id"], name: "index_collections_on_user_id"
create_table "collections_items", id: false, force: :cascade do |t|
t.integer "collection_id", null: false
t.integer "item_id", null: false
end
create_table "item_attributes", force: :cascade do |t|
t.integer "item_id"
t.integer "attribute_id"
t.string "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "item_attributes", ["attribute_id"], name: "index_item_attributes_on_attribute_id"
add_index "item_attributes", ["item_id"], name: "index_item_attributes_on_item_id"
create_table "items", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
But of course performance will suffer due to the many joins. Plus each attribute will be stored as a VARCHAR which means you can't do any numeric comparisons in the database.
If you really need a flexible schema i would instead look into using HSTORE, JSON or another dynamic column type or a schemaless database such as MongoDB.
First you should get very acquainted with what can be done with accepts_nested_attributes_for
and fields_for
and maybe consider using AJAX to delegate the actions out to CollectionController
and a ItemController
on the back end rather than cramming it all into a single monstrosity.
Upvotes: 1