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 }
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.
I have
#total = Purchase::Total.find(1);
Total model have:
has_many :items
belongs_to :member
belongs_to :company
..................
Also companies model has
has_many :addresses
has_one :subscription
..................
and a lot more
How can I get a tree from the #total object containing all the has_one, belongs_to dependencies?
I.E.
<Purchase::Total id: 3, member_id: 4, created_at: \"2015-11-25 14:47:46\", updated_at: \"2015-11-25 14:47:46\", affiliate_company_id: nil, is_paid: false, currency: 1, company_id: 37020, ser_id: 2>
<Company id: 37020, name: \"Andrew\", parent_id: 37019, member_company_id: 37019, payment_company_id: 37019, widget_id: 3003359>
And so ..... (I did the example with: #total.inspect and #total.company.inspect), and I need something like inspect to return automatically all the objects.
Using reflect_on_all_associations
Take a Queue and a Hash and add Total (model name) to it.
Pop a model name, get all associated models and add them queue. Also, using the tablize name of current model, create a new entry in hash and add the tablized names of associated models.
If queue is not empty, go to 2.
At the end, your hash should look like:
{ total: { company: [ :subscription, :addresses ] }, items: { associated: { another: :another_one } } }
Then you can use this in your query:
Total.where().join(hash[:total])
It will fetch all the associated data as well. Then you can simply loop through the attributes. If attribute type is ActiveRecord (or similar), then its an associated model data.
Is it possible to make a query like this? (Pseudo-code)
u=User.includes(all_delegated_attributes_from_relationships).all
How?
Further explanation:
class User<ActiveRecord::Base
has_one :car
delegate :wheel, :brake, :motor, to: :car, prefix: true
end
and then:
u=User.includes(delegated_car_parts).all
#<user id: 1, car_id: 1, name: "John", car_wheel: "foo", car_motor: "bar", car_brake: false>
I know that this can sound a little odd but I have to add a feature to an old app to export all delegated attributes from a model to CSV and this model has 14 relationships and 300 delegations... I just learnt Demeter's law when I made this app...
Assuming wheel, break and motor are relationships on car, you can do this:
User.includes(:car => [:wheel, :brake, :motor]).all
There is no build-in method to do this. You could try sth like:
class User < ActiveRecord::Base
has_one :car
DelegatedCarMethods = [:wheel, :brake, :motor]
delegate *DelegatedCarMethods, to: :car, prefix: true
scope :with_car_delagations, lambda do
select_array = ["#{table_name}.*"]
select_array += DelegateCarMethods.map {|method| "#{Car.table_name}.#{method} AS car_#{method}"}
select(select_array.join(', ')).joins(:car)
end
end
But it isn't extremely pretty. Why do you need this? Calling user.wheel or user.motor doesn't feel right.
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 model called sites. A site will have several adjacent sites (and also be an adjacent site for other sites).
I am trying to determine how best to capture the list of adjacencies.
I have considered creating a text field in the site model and using serialize to store/retrieve the array of all of the adjacent sites. The problem with this is that I'd be violating DRY since there'd be no real relationship formed between the adjacent sites, and thus would have to store the list of adjacent sites for each site individually.
I started digging through some of the online docs for the has_and_belongs_to_many relationship, but in the examples I've found the relationship seems to always be between two different types of objects. Can I have a has_and_belongs_to_many relationship with the same object?
so:
class Site < ActiveRecord::Base
has_and_belongs_to :sites
end
or do I need to create a seperate table for adjacent sites?
Notice that the solution you found works in one direction only:
>> Site.last.friends
[]
>> Site.last.friends << Site.first
[#<Site id: 1, name: "First", description: "The First", created_at: "2009-09-08 21:15:09", updated_at: "2009-09-08 21:15:09">]
>> Site.last.friends
[#<Site id: 1, name: "First", description: "The First", created_at: "2009-09-08 21:15:09", updated_at: "2009-09-08 21:15:09">]
>> Site.first.friends
[]
If you want it to work two ways, you can use something like:
class Site < ActiveRecord::Base
has_and_belongs_to_many :l_adjacent_sites, :class_name => 'Site', :join_table => 'sites_sites', :foreign_key => 'l_site_id', :association_foreign_key => 'r_site_id'
has_and_belongs_to_many :r_adjacent_sites, :class_name => 'Site', :join_table => 'sites_sites', :foreign_key => 'r_site_id', :association_foreign_key => 'l_site_id'
end
But the arcs are directed:
>> Site.first.r_adjacent_sites
[]
>> Site.last.r_adjacent_sites < Site.first
[#<Site id: 1, name: "First", description: "The First", created_at: "2009-09-08 21:15:09", updated_at: "2009-09-08 21:15:09">]
>> Site.last.r_adjacent_sites
[#<Site id: 1, name: "First", description: "The First", created_at: "2009-09-08 21:15:09", updated_at: "2009-09-08 21:15:09">]
>> Site.first.l_adjacent_sites
[#<Site id: 4, name: "Fourth", description: "The fourth", created_at: "2009-09-08 21:48:04", updated_at: "2009-09-08 21:48:04">]
If what you want to represent is directed arcs, you'll be fine; I haven't figured yet a solution for nondirected arcs (apart from mysite.l_adjacent_sites + mysyte.r_adjacent_sites]).
EDIT
I tried to hack something to obtain a adjacent_sites named_scope or the like, but couldn't find anything; also, I'm not sure that a general solution (allowing you to filter results adding more conditions) actually exists.
Since doing l_adjacent_sites + r_adjacent_sites forces the (two) queries execution,
I can only suggest something like:
def adjacent_sites options={}
l_adjacent_sites.all(options) + r_adjacent_sites.all(options)
end
This should allow you to do things like:
#mysite.adjacent_sites :conditions => ["name LIKE ?", "f%"]
There are still issues, though:
Sorting will not work, that is, you'll get a halfsorted set, like [1, 3, 5, 2, 4, 6]. If you need to sort results; you'll have to do it in ruby.
Limit will only half-work: :limit => 1 will give you up to 2 results, as two queries will be executed.
But I'm positive that for most purposes you'll be ok.
It should be possible to do this, however it will involve playing about with the options on
has_and_belongs_to_many
You'll probably have a join table defined with a migration something along the lines of:
create_table :sites_associated_sites, :id => false do |t|
t.integer :site_id
t.integer :associated_site_id
end
Then in the model you'll need to setup has_and_belongs_to_many twice
class Site < ActiveRecord::Base
has_and_belongs_to_many :sites, :join_table => 'sites_associated_sites'
has_and_belongs_to_many :associated_sites, :class_name => 'Site',
:join_table => 'sites_associated_sites'
end
This answer should be taken with a pinch of salt as this is more loud thinking than anything else, but definitely worth having a read up on the has_and_belongs_to_many method in the API: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#M001836
I dug some more and found a good resource for this. The example they used is if you have a class of users, and users can have friends who are also users, but you don't want to create a separate table of friends.
What the author of the article did was create a join table with just the foreign keys, that allows the habtm relationship to exist without a third table.
Here's the link:
http://www.urbanpuddle.com/articles/2007/06/14/rails-mini-tutorial-user-habtm-friends
Some additional digging turned up a method of accomplishing Bidrectional Relationships in the reference book The Rails Way (page 227). Unfortunately there are two problems with the solution in the text. First it was incomplete, though a complete version was available on the book's bug tracker/errata page
class BillingCode < ActiveRecord::Base
has_and_belongs_to_many :related,
:join_table => 'related_billing_codes',
:foreign_key => 'first_billing_code_id',
:association_foreign_key = 'second_billing_code_id',
:class_name => 'BillingCode',
:insert_sql => 'INSERT INTO related_billing_codes (`first_billing_code_id`, `second_billing_code_id`) VALUES (#{id}, #{record.id}), (#{record.id}, #{id})',
:delete_sql => 'DELETE FROM related_billing_codes WHERE (`first_billing_code_id` = #{id} AND `second_billing_code_id` = #{record.id}) OR (`first_billing_code_id` = #{record.id} AND `second_billing_code_id` = #{id})'
end
The second problem is that the solution (which relies on an SQL statement inserting two records into the table within the same INSERT statement) is not supported by the default development database SQLite3.