How to show description fields from related table (2) - ruby-on-rails

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.

Related

Ruby on Rails associations not taking effect

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

Polymorphic Assocations using Integer ID type fields

I have a table Foo that has a polymorphic belongs_to association called bar. The foos table has the standard bar_id column. However, instead of a string-based bar_type column, I have an integer bar_type_id column. This column references the id column in the table bar_types. bar_types.name holds the name of the class that represents the class of the particular bar instance.
Does Rails (ideally >=2.3.10) allow for this type of polymorphic association?
We did it by overriding the association_class method in a new module and included it using the :extend option. Also created a integer to string mapping hash to make things easier.
In config/initializers directory or anywhere you like, create a file and define the hash
INT_OBJECT_TYPE_TO_CLASSNAME = { 0 => "Project", 1 => "Task", 2 => "Timesheet" }
class CommentObjectType < ActiveRecord::Base
module ClassNamesAsInt
def association_class
return INT_OBJECT_TYPE_TO_CLASSNAME[restricted_object_type].constantize
end
end
end
In comments.rb
belongs_to :commentable, :polymorphic => true, :extend => CommentObjectType::ClassNamesAsInt
I'm making use of the polymorphic integer type gem, written by one of my co-workers. It's slightly easier to use than the examples given above, in my opinion. For example, after configuring the mapping, you change from:
belongs_to :actor, polymorphic: true
to the new format:
belongs_to :actor, polymorphic: true, integer_type: true
There are two approaches for this.
First is easy:
has_many :bars, :conditions => "whatever you want"
Second could be tricky:
set_inheritance_column :bar_type_id
I am not sure, but you can play around
belongs_to :bar, :class_name => proc{ BarType.find(self.bar_type_id).name }, :foreign_key => :bar_id

Rails joins or preload belongs_to association from polymorphic model

my problem is following. How can I joins belongs_to association from polymorphic model
There is situation
opinion.rb
class Opinion < ActiveRecord::Base
belongs_to :opinionable, :polymorphic => true
belongs_to :category
end
answer.rb
class Answer < ActiveRecord::Base
has_many :opinions, :as => :opinionable
end
How can i do following
Opinion.joins(:opinionabe).all
it will throw
ArgumentError: You can't create a polymorphic belongs_to join without specifying the polymorphic class!
How can i specific which class i want to join?
Second question. How to preload it?
Opinion.preload(:opinionable).all
works fine. It will do query for each class in belongs_to.
But. if i want to do something like
Opinion.preload(:opinionable => :answer_form).all
there is problem because one model has this association and second hasn't. So it will throw exception.
So how i can do something like
Opinion.preload(:answer => :answer_form, :another_belongs_to_model).all
?
Thanks, David!
Actually if you just do
belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer", conditions: { opinions: { opinionable_type: "Answer"}}
then you can do
Opinion.joins(:opinionable_answer).where(answers: { awesome: true})
It looks like you have not specified opinionable_type:string column for your Opinion model.
Try to update your migration in this manner:
class CreateOpinions < ActiveRecord::Migration
def self.up
create_table :opinions do |t|
t.integer :opinionable_id
t.string :opinionable_type
# ... other fields
t.timestamps
end
end
def self.down
drop_table :opinions
end
end
This will solve your second question and Opinion.preload(:opinionable).all should work well.
You cann't do joins on polymorphic association because they can be located in different tables, which are detected after Opinion model is loaded. That why model needs column opinionable_type.
If you try to do this you'll get next exception
ActiveRecord::EagerLoadPolymorphicError: Can not eagerly load the polymorphic association :opinionable
UPD: Added magic join ^_^
class Opinion < ActiveRecord::Base
belongs_to :opinionable, :polymorphic => true
belongs_to :opinionable_answer, :foreign_key => :opinionable_id, :class_name => "Answer"
scope :by_type, lambda { |type| joins("JOIN #{type.table_name} ON #{type.table_name}.id = #{Opinion.table_name}.opinionable_id AND #{Opinion.table_name}.opinionable_type = '#{type.to_s}'") }
end
Example:
Opinion.by_type(Answer).to_sql
=> "SELECT \"opinions\".* FROM \"opinions\" JOIN answers ON answers.id = opinions.opinionable_id AND opinions.opinionable_type = 'Answer'"
I know this question is old but I just spent an hour looking for the solution to a similar problem (Rails 3) and the only way I got it to work was the solution stated here: https://stackoverflow.com/a/25966630/6878997
Which, in your case would be:
class Opinion < ActiveRecord::Base
# The true polymorphic association
belongs_to :opinionable, polymorphic: true
# The trick to solve this problem
has_one :self_ref, :class_name => self, :foreign_key => :id
has_one :answer, :through => :self_ref, :source => :opinionable, :source_type => Answer
end
Seems tricky but this way you will be able to do multiple chained joins such as:
joins(answer: :other_model).
And whenever opinion.opinionable is not an Answer, opinion.answer will return nil.
Hope it helps somebody!

activerecord find through association

I am trying to retrieve an activerecord object from my db. My models are
class User < ActiveRecord::Base
belongs_to :account
has_many :domains, :through => :account
end
And
class Account < ActiveRecord::Base
has_many :domains
has_many :users
end
And
class Domain < ActiveRecord::Base
belongs_to :account
end
Now I would like to retrieve a user based on the username and a domain name (lets assume that these are attributes of the User and the Domain classes respectively). i.e. something along the lines of
User.find(:first, :conditions =>{:username => "Paul", :domains => { :name => "pauls-domain"}})
I know that the above piece of code will not work since I do have to mention something about the domains table. Also, the association between users and domains is a one-to-many (which probably further complicates things).
Any ideas on how should this query be formed?
If you're using Rails 3.x, the following code would get the query result:
User.where(:username => "Paul").includes(:domains).where("domains.name" => "paul-domain").limit(1)
To inspect what happen, you can append .to_sql to above code.
If you're using Rails 2.x, you'd better write the raw sql query.
The following piece of code did the trick:
User.joins(:account).joins('INNER JOIN "domains" ON "accounts"."id" = \
"domains"."account_id"').where(:users => {"username" => "Paul"}).
where(:domains => {"name" => "paul-domain"})
Sorry about the formatting of this long line of code

Belongs_to based on value of a field

I have a table with entries, and each entries can have different account-types. I'm trying to define and return the account based on the value of cindof
Each account type has one table, account_site and account_page. So a regular belongs_to won't do.
So is there any way to return something like:
belongs_to :account, :class_name => "AccountSite", :foreign_key => "account_id" if cindof = 1
belongs_to :account, :class_name => "AccountPage", :foreign_key => "account_id" if cindof = 2
Have tried to do that in a method allso, but no luck. Really want to have just one accountand not different belongs_to names.
Anyone that can figure out what I want? Hard to explain in English.
Terw
You should be able to do what you want with a polymorphic association. This won't switch on cindof by default, but that may not be a problem.
class ObjectWithAccount < ActiveRecord::Base
belongs_to :account, :polymorphic => true
end
class AccountSite < ActiveRecord::Base
has_many :objects_with_accounts,
:as => :account,
:class_name => 'ObjectWithAccount'
end
class AccountPage < ActiveRecord::Base
has_many :objects_with_accounts,
:as => :account,
:class_name => 'ObjectWithAccount'
end
You will need both an account_id column and a account_type column. The type of the account object is then stored in the extra type column.
This will let you do:
obj.account = AccountPage.new
or
obj.account = AccountSite.new
I would look into Single Table Inheritance. Not 100% sure, but I think it would solve your problem http://code.alexreisner.com/articles/single-table-inheritance-in-rails.html
If that isn't good, this isn't too hard to implement yourself.
def account
case self.cindof
when 1 then AccountSite.find self.account_id
when 2 then AccountPage.find self.account_id
end
end

Resources