I'm brand new to Ruby on Rails and I'm having a heck of a time making sense of my associations.
At my company we rent out Scanner Packs that include scanners and servers.
When we receive a request for a scanner pack, ideally I'd create a new scanner package with the customer info and attach however many scanners and servers are needed.
Here is what I have for my three models, scanner_pack, server and scanner:
scanner_pack.rb:
class ScannerPack < ActiveRecord::Base
attr_accessible :producer, :reserved_from, :reserved_to, :scanner_id, :server_id, :scanner_pack_id
has_many :scanners, :foreign_key => "scanner_id"
has_many :servers, :foreign_key => "server_id"
end
server.rb:
class Server < ActiveRecord::Base
attr_accessible :cat5, :power_cable, :router, :name, :status, :location, :id, :notes, :reserved_from, :reserved_to, :scanner_pack_id
belongs_to :scanner_pack, :class_name => "Server", :foreign_key => "server_id"
end
scanner.rb:
class Scanner < ActiveRecord::Base
attr_accessible :id, :location, :name, :notes, :serial, :status
belongs_to :scanner_pack, :class_name => "Scanner", :foreign_key => "scanner_id"
end
I've googled and searched for quite a while now and I've noticed sometimes people say to remove the attr_accessible for scanner_id and server_id in the scanner_pack model because it will overwrite the association. When I do that, I get the error:
ActiveModel::MassAssignmentSecurity::Error: Can't mass-assign protected attributes:
In the rails console when I attempt to create a new ScannerPack I'm doing something like this:
scanner = Scanner.find(1)
ScannerPack.create(:producer => 12345, :scanner_id => scanner.id)
Then if I try something like:
scannerpack = ScannerPack.find(1)
scannerpack.scanner_id
it returns the correct value for scanner_id
When I try: scannerpack.scanner.id It gives me an undefined method error (I've also tried scannerpack.scanners.id). In my mind it should return the id of the scanner from the scanner object
I'm thinking that I'm either missing something very simple or that I'm completely misunderstanding how to do this. Maybe I should be using a has_and_belongs_to_many assocation? I'd appreciate any help anyone can give!
thanks!
Edit: Here is the whole project on github.
Since you want to have many-to-many association, here is how to achieve this, step by step.
We should create model that will contain scanner_id and scanner_pack_id. Let's name it Pack (I'm sure you will be able to name it better).
rails g model Pack scanner_id:integer scanner_pack_id:integer
rake db:migrate
When you run it, you have to write appripriate "declarations" in your models:
class Pack
belongs_to :scanner
belongs_to :scanner_pack
end
class ScannerPack
has_many :packs
has_many :scanners, through: :packs
end
class Scanner
has_many :packs
has_many :scanner_packs, through: :packs
end
When you're done, you can easily bind scanner_packs with scanner (and vice versa) with (for example):
scanner.scanner_packs << scanner_pack
Remove all other params from belongs_to and has_many methods, leaving only association names.
Error is exactly here
:class_name => "Server"
and here
:class_name => "Scanner"
Reference http://guides.rubyonrails.org/association_basics.html#belongs_to-association-reference (scroll down to 4.1.2 Options for belongs_to and 4.1.2.2 :class_name
Related
I'm in the process of teaching myself Rails and I'm stumped as to why an association isn't working correctly. I think I'm missing something pretty basic but I can't snuff out exactly what.
I have two classes -- Builds and Equipment. Builds are made of 2 pieces of equipment, deviously titled position_1 and position_2. Here's what my definitions look like:
class Build < ActiveRecord::Base
has_one :position_1, :class_name => "Equipment"
has_one :position_2, :class_name => "Equipment"
attr_accessor :position_1, :position_2
end
and
class Equipment < ActiveRecord::Base
belongs_to :build, :foreign_key => :position_1
belongs_to :build, :foreign_key => :position_2
end
(Ignore for the moment that this could be handled by a relationship table to support any number of positions -- I'm basically trying to figure out how to have a class with two has_one relationships to another class.)
Now if I try and do something simple like this....
position_1 = Equipment.find(params[:build][:position_1])
position_2 = Equipment.find(params[:build][:position_2])
#build = Build.new
#build.position_1 = position_1
#build.position_2 = position_2
logger.debug("THE BUILD IS #{#build.inspect}")
I will have successfully created a build object with the equipment objects correctly assigned to the position_1 parameter, but the position_1 and position_2 fields of the build parameter are left nil.
logger.debug("THE EQUIPMENT IS #{#build.position_1}")
> EQUIPMENT IS #<Equipment:0x007fa0581705c0>
logger.debug("THE BUILD IS #{#build.inspect}")
> THE BUILD IS #<Build id: nil, position_1_id: nil, position_2_id: nil, created_at: "2013-05-27 18:00:32", updated_at: "2013-05-27 18:00:32">
What am I getting wrong here?
First, I would not include the associations as "attr_accessors" in the "parent" ActiveRecord. Rails creates the correct association field for Build for you.
When you create instances of the positions, like an instance of Equipment, the correct way to assign them to a new Build would be:
position_1 = Equipment.find(params[:build][:position_1])
position_2 = Equipment.find(params[:build][:position_2])
#build = Build.new
#build.position_1 << position_1
#build.position_2 << position_2
Here's good guide on Rails associations.
Figured it out -- I had misunderstood part of how Rails does associations -- what I really needed was...
class Equipment < ActiveRecord::Base
has_many :builds, :foreign_key => :position_1
has_many :builds, :foreign_key => :position_2
end
class Build < ActiveRecord::Base
belongs_to :position_1, :class_name => "Equipment", :foreign_key => "position_1_id"
belongs_to :position_2, :class_name => "Equipment", :foreign_key => "position_2_id"
end
With this in place everything works as I expected.
Just started playing with Ruby (no IT background) and restarted the project based on previous question / answer (link). I have got the following situation now:
created a currencymaster table with the following columns id:integer , description:string and isocode:string whereby the ID column is created by Ruby.
created a currencyrates table with the following columns id:integer , dominant:integer and converted:integer and rate:decimal whereby the ID column is created by Ruby.
Based on help on this site I created the following models.
The models/currencymaster.rb looks like this:
class Currencymaster < ActiveRecord::Base
has_many :currency_rates_dominant, :validate => true, :class_name => 'Currencyrate'
has_many :currency_rates_converted, :validate => true, :class_name => 'Currencyrate'
end
The models/currencyrate.rb looks like this:
class Currencyrate < ActiveRecord::Base
belongs_to :currency_master_doms, :class_name => 'Currencymaster'
belongs_to :currency_master_convs, :class_name => 'Currencymaster'
end
I haven't changed anything yet in the both controllers.
The views\currencyrates\index.html.erb is generated automatically via Ruby and is showing the values of the records as integer. The goal is to show the currencymaster.iso value out of the Currencymaster table for both currencyrate.dominant and currencyrate.converted
Thanks a lot!!
I think you should change your class like this :
class Currencyrate < ActiveRecord::Base
belongs_to :currency_master_dom, :class_name => 'Currencymaster', :foreign_key => 'dominant'
belongs_to :currency_master_conv, :class_name => 'Currencymaster' , :foreign_key => 'converted'
end
After that you should do this :
rate = Currencyrate.first
rate.currency_master_dom.iso
rate.currency_master_conv.iso
All the conventions aren't respected. You should use dominant_id and converted_id as forign keys, CurrencyRate and CurrencyMaster for class names and you shouldn't have a 's' whan you use belongs_to.
I have the following models:
class MenuItem < ActiveRecord::Base
has_one :price, :as => :pricable
accepts_nested_attributes_for :price
attr_accessible :price_attributes, :price
end
class Price < ActiveRecord::Base
belongs_to :pricable, :polymorphic => true
attr_accessible :price, :price_comment
end
I'm trying to insert at rails console with:
MenuItem.create({"name"=>"Julie's Mac & Cheese","price"=>{"price"=>14}})
but am getting this error:
ActiveRecord::AssociationTypeMismatch: Price(#70145189558680) expected, got Hash(#70145158648700)
How do I force this into thinking it is a Price? tried using it as symbol
thx?
MenuItem.create(:name => "Julie's Mac & Cheese", :price_attributes => {:price => 14})
edit Sorry for lack of explanation but the answer is pretty simple... you just weren't passing the right parameter. I also cleaned it up a bit.
I'm currently writing some intranet web application where people could submit to admins requests for adding different resources. The example requests would be:
installing programs, in this case user will select which program he wants installed
increasing quota, in this case user will just enter the amount of disk space he needs or maybe he will select the predefined quantities - 1GB, 10GB etc...
create new email alias, in this case user will just type the alias.
...
I was thinking about having just one model UserRequests with the reference to the sender and
two optional attributes one would be reference_id that would refefrence to other tables (for
example the Program that he wants installed) and another would be used for free type fields
like email alias or quota.
So my problem is that based on the type of the request the model should contain either:
reference to other table
integer data
string data
Based on the type of the request the given action should be taken - probably email alias
could be added from rails but the application on users computer will be installed by hand.
Does anyone had similar problem? Do you think using polymorphism for this kind of stuff is a good idea? Do you have any suggestions on how to organize data in the tables?
Single Table Inheritance! This way you can have each type of request have custom validations, while still having every request live in the same table.
class CreateUserRequests < ActiveRecord::Migration
def self.up
create_table :user_requests do |t|
t.string :string_data, :type
t.integer :user_id, :integer_data
t.timestamps
end
end
def self.down
drop_table :user_requests
end
end
class UserRequest < ActiveRecord::Base
belongs_to :user
end
class EmailAliasRequest < UserRequest
validates_presence_of :string_data
validates_format_of :string_data, :with => EMAIL_REGEX
end
class ProgramInstallRequest < UserRequest
belongs_to :program, :class_name => "Program", :foreign_key => "integer_data"
validates_presence_of :integer_data
end
class QuotaIncreaseRequest < UserRequest
validates_presence_of :string_data
validates_inclusion_of :string_data, :in => %w( 1GB 5GB 10GB 15GB )
end
And of course, alias your string_data and integer_data to email or whatnot to make your other code have a little more meaning. Let the model be the little black box that hides it all away.
I would use polymorphic associations, which let a model belong to more than one other model using a single association. Something like this:
class AdminRequest < ActiveRecord::Base
belongs_to :user
belongs_to :requestable, :polymorphic => true
end
class EmailAlias < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
class ProgramInstall < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
class QuotaIncrease < ActiveRecord::Base
has_many :admin_requests, :as => :requestable
end
As ever, Ryan Bates has an excellent Railscast on the subject.
I have the following models.
# app/models/domain/domain_object.rb
class Domain::DomainObject < ActiveRecord::Base
has_many :links_from, :class_name => "Link", :as => :from, :dependent => :destroy
end
# app/models/link.rb
class Link < ActiveRecord::Base
belongs_to :from, :polymorphic => true
belongs_to :object_value, :polymorphic => true
end
Problem is, when I do the following, the from_type doesn't prefix the Domain namespace to the model e.g.
Domain::DomainObject.all(:include=> :links_from )
That causes the following SELECT:
SELECT `links`.* FROM `links` WHERE (`links`.`from_id` IN (5,6,12,13,18,24,25,27,29,30,31,32,34,35,39) and `links`.`from_type` = 'DomainObject')
The query should be:
SELECT `links`.* FROM `links` WHERE (`links`.`from_id` IN (5,6,12,13,18,24,25,27,29,30,31,32,34,35,39) and `links`.`from_type` = 'Domain::DomainObject')
because Rails automatically saves the model with the namespace.
I've seen a few recommendations on Rails sites about doing something like this:
belongs_to :from, :polymorphic => true, :class_name => "Domain::DomainObject"
However, that doesn't appear to work either.
So, is there a better way to do this? Or is this not supported?
To fix this, I did a include Domain in the DomainObject model and set ActiveRecord::Base.store_full_sti_class = true in config/environment.rb.
hoyhoy's response is the solution. This solved my problem , too (I actually wanted the namespace to be stripped off).
However, I'd recommend that x.store_full_sti_class = true be added to config/environment.rb, only if it is desired globally. I imagine it might not always be required, in which case, we can easily translate the solution to the class level.
class User < ActiveRecord::Base
self.store_full_sti_class = true
...
end