Simple association question.
I have a Location model with the following fields:
- Name
- Address
- Lat
- Long
This model should have many "locationables".
Ok. I also have the following models:
Lodging
has_one location (location id)
Transportation
has_one start_location, class_name: location
has_one end_location, class_name: location
So, in this situation I should have a "belongs_to" in my Location model? Or I don't need anything and just put "belongs_to" in each other model?. That seems wrong, right? It seems so simple yet my head is not solving it.
To create proper cross reference you need to have either ::belongs_to or ::has_and_belongs_to_many associations in a class to initiate a direct reference. The first creates class_id field (and attribute readers) in the class, the second additional cross reference join table named like "class_klasses". If you have no eithers, you can properly setup the reference by tying the classes, because ::has_one, and ::has_many just use back references, not direct ones. So you will need something like follows:
class Locationable < ...
belongs_to :location
end
class Lodging < ...
belongs_to :location
end
class Transportation < ...
belongs_to start_location, class_name: :Location
belongs_to end_location, class_name: :Location
end
or course to keep the db more clean you can use through key for (for example) Lodging class, but only then if you already have an assotiation in it:
class Locationable < ...
belongs_to :location
end
class Lodging < ...
belongs_to :locationable
has_one :location, through: :locationable
end
Related
I have a question about Rails Nested Attributes.
I'm using Rails 4 and have this model:
model Location
has_one parking_photo
has_many cod_photos
accepts_nested_attributes_for :parking_photo
accepts_nested_attributes_for :cod_photos
end
When I use for example:
Location.find(100).update(cod_photo_ids: [1,2,3]) it works.
But Location.find(100).update(parking_photo_id: 1) doesn't works.
I don't know what difference between nested attributes has_one and has_many.
Or do we have any solution for my case, when I already have child object and want to link the parent to the child and don't want to use child update.
Thank you.
The problem has nothing to do with nested attributes. In fact you're not even using nested attributes at all in these examples.
In this example:
Location.find(100).update(cod_photo_ids: [1,2,3])
This will work even if you comment out accepts_nested_attributes_for :cod_photos as the cod_photo_ids= setter is created by has_many :cod_photos.
In the other example you're using has_one where you should be using belongs_to or are just generally confused about how you should be modeling the association. has_one places the foreign key on the parking_photos table.
If you want to place the parking_photo_id on the locations table you would use belongs_to:
class Location < ActiveRecord::Base
belongs_to :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
has_one :location # references locations.parking_photo_id
end
Of course you also need a migration to actually add the locations.parking_photo_id column. I would really suggest you forget about nested attributes for the moment and just figure out the basics of how assocations work in Rails.
If you really want to have the inverse relationship and put location_id on parking_photos you would set it up like so:
class Location < ActiveRecord::Base
has_one :parking_photo
# ...
end
class ParkingPhoto < ActiveRecord::Base
belongs_to :location
validates_uniqueness_of :location_id
end
And you could reassign a photo by:
Location.find(100).parking_photo.update(location_id: 1)
I have a number of associated tables in an application
class Listing < ActiveRecord::Base
belongs_to :house
belongs_to :multiple_listing_service
end
class House < ActiveRecord::Base
has_one :zip_code
has_one :primary_mls, through: :zip_code
end
I wanted to create a scope that produces all the Listings that are related to the Primary MLS for the associated House. Put another way, the scope should produce all the Listings where the multiple_listing_service_id = primary_mls.id for the associated house.
I've tried dozens of nested joins scopes, and none seem to work. At best they just return all the Listings, and normally they fail out.
Any ideas?
If I understand correctly, I'm not sure a pure scope would be the way to go. Assuming you have:
class MultipleListingService < ActiveRecord::Base
has_many :listings
has_many :zip_codes
end
I would go for something like:
class House < ActiveRecord::Base
...
def associated_listings
primary_mls.listings
end
end
Update 1
If your goal is to just get the primary listing then I would add an is_primary field to the Listing. This would be the most efficient. The alternative is a 3 table join which can work but is hard to optimize well:
class Listing < ActiveRecord::Base
...
scope :primary, -> { joins(:houses => [:zip_codes])
.where('zip_codes.multiple_listing_service_id = listings.multiple_listing_service_id') }
Say I have this model and associated schema defined.
class Memory < ActiveRecord::Base
belongs_to :memory_slot
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
Now typically it let me can access memory slots of Memory via #memory.memory_slot.name. But I want to access it via different method like #memory.supporting_memory_slot.name. What is the best way I can do that?
You won't need any new migration, you can use the previous memory_slot_id, and still can change the name like following:
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, class_name: 'MemorySlot', foreign_key: 'memory_slot_id'
end
class MemorySlot < ActiveRecord::Base
has_many :memories
end
This way, if you had any records saved previously, they will work in the current scenario as well. But if you generate a new migration, the old records saved will not be accessible, because they were used using the foreign_key as memory_slot_id.
If you can change your model association like this
class Memory < ActiveRecord::Base
belongs_to :supporting_memory_slot, :class_name => 'MemorySlot', :foreign_key => 'supporting_memory_slot_id'
end
then you can do something like this
#memory.supporting_memory_slot.name
Note: In this case,you must generate a new migration to add supporting_memory_slot_id to your memories table
I am quite new in the ruby on rails world. I have two classes, A and B and defining in the following way:
class AClass < ActiveRecord::Base
has_many :a_b_class
end
class ABClass < ActiveRecord::Base
validates_presence_of :attr1, :attr2
belongs_to :a_class
belongs_to :b_class
attr_accessible :attr1,:attr2,:a_class, b_class
end
class BClass < ActiveRecord::Base
validates_presence_of :attr4, :attr5
has_many :a_b_class
attr_accessible :attr4,:attr5
end
I am using activeadmin to administrate the databases data, etc.
The problem is that activeadmin allows me to delete a BClass object that is referenced by a AClass (through ABClass relationship) object so when I enter to the http://example.com/a_class the view failed because the view try to access to attr1 of a nil element. How can I add validation to the model in order to avoid delete a referenced object?
EDITED: I corrected the relationship, is a many to many
I think your association is wrong somewhere first rectifie that e.g article has many comments,so comment has article_id,and comment has validates_presence_of :article_id not article and if you delete article respective comment should be deleted there for you need 'dependent=>:destroy"
class Article <AR
has_many :comments,:dependent=>:destroy
end
class Comment <AR
belongs_to :article
attr_accessible :article_id,....
end
I am not sure this will solve the problem - but can you try adding a belongs_to attribute to BClass to mark that it belongs to AClass?
belongs_to :a_class
Alsoo, you can't do this:
validates_presence_of :b_class_id
since there could be many ids..
I've hit something that I don't understand how to model with Rails associations and neither STI nor polymorphism seem to address it.
I want to be able to access attributes from a join table via the collection that's created by has_many :through.
In the code below, this means that I want to be able to access the name and description of a committee position via the objects in the .members collection but as far as I can see I can't do that. I have to go through the original join table.
e.g. modelling a club and it's committee members
class User < ActiveRecord::Base
attr_accessible :full_name,
:email
has_many: committee_positions
has_many: committees, :through => committee_positions
end
class Committee < ActiveRecord::Base
attr_accessible :name
has_many :committee_positions
has_many :members, :through => :committee_positions
end
class CommitteePosition < ActiveRecord::Base
attr_accessible :user_id,
:committee_id,
:member_description,
:role_title
belongs_to :committee
belongs_to :user
end
Assume that each committee position instance has a unique description
i.e. the description is particular to both the member and the committee and so has to be stored on the join table and not with either the user or the club.
e.g.
Committee member: Matt Wilkins
Role: "Rowing club president"
Description: "Beats the heart of the rowing club to his own particular drum"
Is there a way to access the data in the join table via the committee.members collection?
While active record gives us this great alias for going directly to the members, there doesn't seem to be any way to access the data on the join table that created the collection:
I cannot do the following:
rowing_committee.members.find_by_role_title('president').name
Each item in the .members collection is a user object and doesn't seem to have access to either the role or description that's stored in the CommitteePositions join table.
The only way to do this would be:
rowing_committee.committee_positions.find_by_role_title('president').user.name
This is perfectly do-able but is clunky and unhelpful. I feel like the use-case is sufficiently generic that I may well be missing something.
What I would like to access via objects in the committee.members collection
member
- full_name
- email
- role_title (referenced from join table attributes)
- member_description (referenced from join table attributes)
This is only a small thing but it feels ugly. Is there a clean way to instruct the "member" objects to inherit the information contained within the join table?
-------------- addendum
On working through this I realise that I can get half way to solving the problem by simply defining a new class for committee member and referencing that instead of user in the has_many :through relationship. It works a little bit better but is still pretty clunky
class Committee < ActiveRecord::Base
...
has_many :committee_positions
has_many :members,
:through => :committee_positions,
:class_name => 'CommitteeMember'
...
end
class CommitteeMember < User
def description( committee )
self.committees.find_by_committee_id( committee.id ).description
end
def role( committee )
self.committees.find_by_committee_id( committee.id ).description
end
end
Now this is getting closer but it still feels clunky in that the code to use it would be:
committee = Committee.first
president_description = committee.members.find_by_title('president').description( committee )
Is there any way to initialize these objects with the committee they are referencing?
I think you could use some delegation here. In your Committee_Position class:
class Committee_Position < ActiveRecord::Base
attr_accessible :user_id,
:committee_id,
:member_description,
:role_title
belongs_to :committee
belongs_to :user
delegate :name, :email, :to => :user
end
so you could do what you say you want:
rowing_club.committee_members.find_by_role_title('president').name