Polymorphic, STI or other - ruby-on-rails

I am unsure of the best way to approach this scenario and would appreciate some direction. Essentially I have a scenario whereby I need to record assets for an organisation.
There are various types of assets so the attributes differ from type to type however there are a number of fields common to all assets such as:
location
make
model
colour
purchase_date
warranty_period
Similar to:
How to design a product table for many kinds of product where each product has many parameters
I had through of creating this as
one-to_many between Organisation and Asset
polymorhpic between Asset and Details
class Organisation < ApplicationRecord
has_many :assets
end
class Asset < ApplicationRecord
belongs_to :organisation
belongs to :detail, polymorphic: true
end
class CarAsset < ApplicationRecord
has_one :asset, as: :detail
end
class ComputerAsset < ApplicationRecord
has_one :asset, as: :detail
end
My question is:
I wish to create the asset & detail in a single form action so the user after selecting the asset type makes a single form entry for both models.
The user would click a link on the organisation show page:
<%= link_to "New car asset", new_organisation_asset_path(#organisation, query: :new_car_asset) %>
The in my controller I could do something similar to:
class AssetsController < ApplicationController
def new
#organisation = Organisation.find(params["organisation_id"])
#asset = #organisation.assets.new
case params[:query]
when "new_car_asset"
#details = #asset.car_assets.new
when "new_computer_asset"
#details = #asset.computer_assets.new
end
end
end
In my view I could also check the value of params[:query] and render the corresponding form partial that relates to the asset type.
Is this going down the correct path or is there a better way to achieve this? It does feel quite clunky.

I think it would be maybe better to use has_many :trough, should get more out of it in a long run. Like this:
class Organisation < ApplicationRecord
has_many :cars, through: assets
has_many :cumputers, through: assets
has_many :locations, through: assets
has_many :purchase_date, through: assets
end
class Asset < ApplicationRecord
belongs_to :organisation
belongs_to :cars
belongs_to :cumputers
belongs_to any :locations
belongs_to :purchase_date
end
class Car < ApplicationRecord
has_one :organisation, through: assets
end
class Cumputer < ApplicationRecord
has_one :organisation, through: assets
end
class Location < ApplicationRecord
has_one :organisation, through: assets
end
class Purchase_date < ApplicationRecord
has_one :organisation, through: assets
end
Then you can create Assets within Organisations_controller and can nest everything in Organisation form with fields_for. Assets model would contain references between Organisation and every single detail model, but everything would be sepperate if you wonna do more with it in views or special fields.

Related

rails helper method to find if article belongs to a certain category

I recently updated my knowledgebase app to have a pretty standard has_many through association. Previously articles belonged to a category and a category had many articles. Now it is situated as the following:
class Article < ApplicationRecord
has_many :categorizations
has_many :categories, :through => :categorizations
class Category < ApplicationRecord
has_many :categorizations
has_many :articles, :through => :categorizations, dependent: :destroy
class Categorization < ApplicationRecord
belongs_to :article
belongs_to :category
One of the categories I have is the "Internal" category. What I am trying to accomplish is a helper method that I can us to perform actions if a particular article has the Internal category set on it. Something like the following:
if #article.internal?
do stuff
end
I am thinking it need to be in the articles_helper.rb file.
module ArticlesHelper
def internal?
figure out if #article has category "internal"
end
end
I have the following but am thinking I am on the wrong track and could use some help:
def internal?
#internal_cat = []
#article.categories.each do |n|
if (n.name == "Internal")
#internal_cat = n.name
end
end
end
Any help would be greatly appreciated!
This is the wrong use case for a helper method. Helper methods are used to simplify and DRY your views, and occasionally controllers. For example the form helpers make it easier to create forms and bind models to inputs.
Helpers are modules that are mixed into the view context.
What you want here is just a plain old method on your model as it can act on self:
class Article < ApplicationRecord
# ...
def has_category?(name)
categories.exists?(name: name)
end
def internal?
has_category?("Internal")
end
end
Later you can refactor this code into a module if needed but this is not the same thing as a helper. Rather its parallel inheritance through the mixin pattern.
module Categorized
extend ActiveSupport::Concern
included do
has_many :categorizations, as: :resource
has_many :categories, through: :categorizations
end
def has_category?(name)
categories.exists?(name: name)
end
end
class Article < ApplicationRecord
include Categorized
end
class Video < ApplicationRecord
include Categorized
end
You also want to set the dependent: :destroy option on the categorizations join table. The way you have set it up destroying an article will destroy the normalized row in categories!
class Article < ApplicationRecord
has_many :categorizations, dependent: :destroy
has_many :categories, through: :categorizations
class Category < ApplicationRecord
has_many :categorizations, dependent: :destroy
has_many :articles, through: :categorizations

How to get relation records over relation table issue

When I developed my first sandbox application, I wanted to get some records for relational table.
User.rb:
class User < ActiveRecord::Base
#has many followed articles
has_many :follow_articles
And FollowArticle model:
class FollowArticle < ActiveRecord::Base
belongs_to :user
belongs_to :article
end
And Article model:
class Item < ActiveRecord::Base
has_many :follow_articles
end
I want to get all followed articles of a user so in my controller I have:
#articles = current_user.follow_articles
which gave me:
ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_FollowArticle:x3014X2
In my view I can iterate over these articles:
<% #articles.each do |article| %>
<%= article.article.name %>
<% end %>
which works perfectly.
Can I do this in this way to get an Articles array instead of a FollowArticles array, something like:
#items = current_user.follow_articles
to return articles instead of followArticles?
Use has_many :through.
From the Guide:
A has_many :through association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding through a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this:
class Physician < ApplicationRecord
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ApplicationRecord
belongs_to :physician
belongs_to :patient
end
class Patient < ApplicationRecord
has_many :appointments
has_many :physicians, through: :appointments
end
So, fully connecting the dots...
#User.rb
class User < ActiveRecord::Base
has_many :follow_articles
has_many :followed_articles, through: :follow_articles
end

Rails: ActiveRecord has_many association not working

I'm a bit new to Rails Active Record associations. I've tried to set up a relationship, but I get ActiveRecord error when I try to retrieve data. Did I associate my models incorrectly?
User has many Uploads, which has many UserGraphs:
class User < ActiveRecord::Base
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I want to get all of a user's uploads, and all of a user's graphs. The 2nd line doesn't work in rails console and gives an error
#user = User.find(1)
#uploads = #user.uploads
The error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_graphs in model User
Extra Credit:
If Users have Uploads that have UserGraphs... shouldn't it be has_many :uploads and has_many :user_graphs, through :uploads?
Add
has_many :user_graphs
to the User and Upload classes.
The :through option defines a second association on top of this one.
You didn't tell Rails that you have a user_graphs association on User, only an uploads association. So when Rails goes to follow the user_graphs association on uploads, it can't find it.
So, you need add the user_graphs association. Your models should look like this:
class User < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end

Association dilemma

What is the best way to implement something like a board in pinterest (a collection of objects) in rails. I am trying to come with it, it seems more like an array implementation.
Here's my logic for the associations : User have many collections, user have many pins , collections belongs to user.
User class
class User < ActiveRecord::Base
has_many :pins, through: :collections
has_many :collections
end
Pins class
class Pin < ActiveRecord::Base
belongs_to :user
has_many :collections
end
Collections class
class Collection < ActiveRecord::base
belongs_to :user
end
So now here's my confusion, how to implement a controller that will allow me to create a collection and inside this collection object, create or push pins and save them as another object for the current_user. Hope I'm making sense
Here's the controller
class CollectionsController < ApplicationController
def create
#collection = current_user.collections.new(params[:collection])
#this where i'm confused , if it an array , how to implement it , to push or create a pin object inside ?
end
end
You have to use nested attributes for this.
Check this http://currentricity.wordpress.com/2011/09/04/the-definitive-guide-to-accepts_nested_attributes_for-a-model-in-rails-3/.
Basically what you need is:
# collection model
accepts_nested_attributes_for :pins
# view, see also nested_form in github
f.fields_for :pins
You're looking for the has_many_through association. See section 2.4 on the Rails guide: http://guides.rubyonrails.org/association_basics.html
class User < ActiveRecord::Base
has_many :collections
end
class Pin < ActiveRecord::Base
has_many :collections, through: :pinnings
end
class Pinning < ActiveRecord::Base
belongs_to :pin
belongs_to :collection
end
class Collection < ActiveRecord::base
belongs_to :user
has_many :pins, through: :pinnings
end

Active record: Querying nested many to many models

I have a Store that has many Catalogs and in turn each catalog has many Products.
The Product and Catalog shares many to many relationship.
One product can belong to many catalogs and vice-verse.
So, my model definition goes like this:
class Store < ActiveRecord::Base
has_many :store_catalogs
has_many :catalogs, :through => :store_catalogs
end
class StoreCatalog < ActiveRecord::Base
belongs_to :store
belongs_to :catalog
end
class Catalog < ActiveRecord::Base
has_many :store_catalogs
has_many :stores, :through => :store_catalogs
has_and_belongs_to_many :product_set, :class_name => "Product"
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :catalogs
end
Now, I would like to access all the products that belongs to a store.
How do I link those together so that I get that? please suggest.
I am trying out various combinations from the rails console to pull the data, not sure if the console itself limits the relation based querying by any chance(though i would like to believe it doesn't).
I think this should solve your problem
class Store < ActiveRecord::Base
has_many :catalogs
has_many :products, :through => :catalogs
end
class Catalog < ActiveRecord::Base
belongs_to :store
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :catalogs
end
And then you just need a table in your database called catalogs_products with catalog_id and product_id to link the has_and_belongs_to_many association.
Then in order to get all the products from a store just do
Store.find(id).products

Resources