I have 2 models, Passenger and Location
class Location < ActiveRecord::Base
has_many :passengers
end
and
class Passenger < ActiveRecord::Base
has_one :location
end
I insert some data in rails console
p1 is passenger record
p1
=> #<Passenger id: 2, name: "Saba", lastname: "las", phone: 1234, created_at: "2015-11-15 13:04:03", updated_at: "2015-11-15 13:04:03", id_location: 1>
and l1 is a location record
l1
=> #<Location id: 1, latitude: 1.4, longitude: 4.5, created_at: "2015-11-15 13:02:00", updated_at: "2015-11-15 13:02:00">
"id_location" is a foreign key for location in Passenger
I did p1.save and l1.save.
i expect p1.l1 gives me the complete data including l1 record like(Location id: 1, latitude: 1.4)
instead it gives me the following erorr:
NoMethodError: undefined method `l1' for #<Passenger:0x000000022b96c8>
Looks like your foreign_key is wrong; Rails default foreign_keys are association_id (not id_association, as you have it).
--
To fix it, you'll either need to set the foreign key explicitly (and call the correct associative object), or rename the id_location column in your table:
#app/models/passenger.rb
class Passenger < ActiveRecord::Base
belongs_to :location, foreign_key: :id_location
end
You should also note the belongs_to association call -
With ActiveRecord, if you have the foreign key in a model, that model needs to belong to another. Using has_one suggests you should put the foreign key in the associated model, which is incorrect in this case.
--
Finally, you also need to make sure you're calling the associated objects by the correct name.
If your Passenger belongs_to Location, you will need to call #passenger.location
Related
Heres my relationship model
class Address < ApplicationRecord
has_many :address_aliases, :inverse_of => :address, :foreign_key => :address_id
end
In spec I am building an address with address_aliases. In my after_save of address I have used address.address_aliases.pluck somewhere, and it does not give correct value.
address_aliases = FactoryGirl.build_list(:address_alias, 1, :alias_for_city => "TEST1")
address = FactoryGirl.build(:some_address, :company_id => "test_company", :address_aliases => address_aliases)
byebug
expect ...
address.save!
This is what I get on byebug. address.address_aliases has one element, but when I pluck it returns blank array.
(byebug) address.address_aliases
#<ActiveRecord::Associations::CollectionProxy [#<AddressAlias id: nil, alias_for_city: "TEST1", created_at: nil, updated_at: nil, address_id: nil>]>
(byebug) address.address_aliases.pluck(:alias_for_city)
[]
The problem is that the aliases are not yet persisted and pluck does a database query (see when you inspect address_aliases, the record does not have an ID yet, it's on memory, it's not on the database yet).
Replace that pluck with map(&:alias_for_city) so it doesn't do a database query and uses the already loaded collection.
Situation:
I have 4 models with relationships like these:
class A < ActiveRecord::Base
belongs_to :b
belongs_to :c
belongs_to :d
end
class B < ActiveRecord::Base
has_many :as, dependent: :destroy
end
class C < ActiveRecord::Base
belongs_to :d
end
class D < ActiveRecord::Base
has_many :cs, dependent: :destroy
end
Problem:
I need to relate model A object a to model B object b but I also need to set a position for object a where to appear in ActiveRecord CollectionProxy when calling b.as and it won't work. All attempts to add object a to a certain position in a.bs result end up with having newly added a at the end. Is it possible at all? I know there are possibilities for defining ordering in arrays and while querying, but is it possible to define the ordering directly when adding new relationship?
Example:
Model B object b has 3 model A objects.
b.as
=> #<ActiveRecord::Associations::CollectionProxy [
#<A id: 39038, quantity: 3.0 created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4144, c_id: nil, b_id: 81218>,
#<A id: 39039, quantity: 2.0, created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4145, c_id: nil, b_id: 81218>,
#<A id: 39040, quantity: 2.0, created_at: "2016-02-01 15:51:26", updated_at: "2016-02-01 15:51:26", d_id: 4145, c_id: 1590, b_id: 81218>]>
I create fourth model A object a and I need it to appear on second place when calling b.as like this:
a = A.create(quantity: 4.0, d: D.find(4144), c: C.find(1612), b: b)
Basically the ordering should be defined like this:
b.as
=>
a1: d_id: 4144, c_id: nil
a2: d_id: 4144, c_id: 1612
a3: d_id: 4145, c_id: nil
a4: d_id: 4145, c_id: 1590
...meaning that every a object with same d_id attribute should be grouped together meanwhile having those as that have c_id.nil? in first place.
The add method will always add it to the end of the list. You need some sort of a position attribute to achieve what you want. If you don't want to implement that yourself, you can take a look at Acts as list gem. Another option is to add a default_scope { order: :key_to_order_by } on the As model.
has_many :as, -> { order(d_id: :asc, c_id: :asc) }
Should be close, except nil is ordered at the end after the highest cd_id. That's postgres doing the sort, so I don't know if there is much you can do there.
I have a model Camera in which
belongs_to :user, :foreign_key => 'owner_id', :class_name => 'EvercamUser'
i have asscociation like this. when i do Camera.first
#<Camera id: 6, created_at: "2013-12-12 17:30:32", updated_at: "2015-11-19 10:19:33", exid: "dublin-rememberance-floor2", owner_id: 4, is_public: true
i can get owner id, is there any way to create such function that , along side getting owner id, i can get the data which linked with this id for example at id = 4
#<EvercamUser id: 4, created_at: "2013-12-12 16:43:46", updated_at: "2015-04-16 15:23:19", firstname: "Garrett", lastname: "Heaver", username: "garrettheaver"
this user is present, what if when i do Camera.first then instead of OnwerID, how can i get the owners Name?
Any help will be appreciated!
how can i get the owners Name
You'd call the associative object on the Camera object:
#camera = Camera.find x
#user = #camera.user
#user.name #-> outputs name of associated user object
... this will allow you to call the attributes of the child object on it: #camera.user.name or #camera.user.email, etc
Off topic, but I always include a reference to delegate for this type of issue; it avoids the law of demeter (where you're using more than one point to access data).
This would allow you to use:
#app/models/camera.rb
class Camera < ActiveRecord::Base
belongs_to :user, foreign_key: :owner_id, class_name: 'EvercamUser'
delegate :name, to: :user, prefix: true #-> #camera.user_name
end
#camera = Camera.find x
#camera.user_name #-> outputs the user's name on the camera object (not user object)
To give you some context, Rails uses ActiveRecord to invoke/create objects for you.
In line with the object orientated nature of Rails, ActiveRecord is known as an ORM (Object Relationship Mapper). This basically allows you to create an object through ActiveRecord, and if it is associated to another (as Rails does with its associations), it will append the associated object onto the parent.
Thus, when you're asking about calling owner_id, you're referring to the foreign_key of the association (the database column which joins the two tables together):
What you need is to reference the associated object, which I've detailed above.
What about using join here?
Camera.all.joins(:evercamusers)
Camera.where(:id => 1).joins(:users).first
Note: I'm a bit unsure if the correct parameter should be ":users" or ":evercamusers"
http://apidock.com/rails/ActiveRecord/QueryMethods/joins
You could also add methods to your class to do this.
class Camera < ActiveRecord::Base
belongs_to :user, :foreign_key => 'owner_id', :class_name => 'EvercamUser'
def firstname
self.user.firstname
end
end
When you try to output data from Camera like this:
#<Camera id: 6, created_at: "2013-12-12 17:30:32", updated_at: "2015-11-19 10:19:33", exid: "dublin-rememberance-floor2", owner_id: 4, is_public: true
It won't show. But if you call the method like this, it should work:
Camera.first.firstname # "Garrett"
Also, if JSON is acceptable you could override the as_json method.
def as_json(options={})
{ :firstname => self.user.firstname }
end
Then call it with
Camera.first.as_json
If you need to do it with all, simply loop it
Camera.all.each { |c| puts c.firstname }
So here is an example of a hash for a record of the 'properties' table, the attribute in question being 'owner'
Property.first #=>
#<Property id: 3684, ss_property_id: 1, owner_full_name: "Holliday Associates", owner: "HA",
owners_pctg: 100, tax_map_id: "0460001047", county: "Horry", description: "L.S. Alford", acreage:
131.0, prop_taxes_2009: 180.72, prop_taxes_2010: 173.99, prop_taxes_2011: 172.94, notes: nil,
created_at: "2013-04-03 01:16:23", updated_at: "2013-04-03 01:16:26">
When I do something like this, however
1.9.3p194 :011 > Property.first.owner
Property Load (0.3ms) SELECT "properties".* FROM "properties" LIMIT 1
=> nil
it is nil.
EDIT: here is my model (dramatically over-simplified...)
class Property < ActiveRecord::Base
belongs_to :owner
end
My model ended up having a conflict with the :owner namespace. It actually belongs_to :owner, a new model I haven't even started using yet. The :owner namespace apparently got overwritten by the ActiveRelation method to the Owner model
I have a number of Rails models, each of which has_one :myobj. I'd like to be able to determine at runtime, what the foreign key is for each of those associations. Is this possible?
I am not trying to duplicate this question. I want the foreign key from a specific model's viewpoint, not the default foreign key for a given mode.
You might be looking for the method reflect_on_association.
On a model that looks like this:
class Foo < ActiveRecord::Base
has_one :myobj, foreign_key:'myobj_id', class_name:'Bar'
end
Running the method gives me this:
1.9.3-p194 :001 > pp Foo.reflect_on_association(:myobj)
#<ActiveRecord::Reflection::AssociationReflection:0x96669f4
#active_record=
Foo(id: integer, name: string, created_at: datetime, updated_at: datetime),
#collection=false,
#macro=:has_one,
#name=:myobj,
#options={:foreign_key=>"myobj_id", :class_name=>"Bar"},
#plural_name="myobjs">