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)
Related
I have a model User, that has_one :child, and the Child model has_one :toy.
If I have a single instance of the User class user, how can I load both the child and toy in one query?
Here's what doesn't work:
user.child.toy # 2 queries
user.includes(child: :toy) # can't call includes on a single record
user.child.includes(:toy) # same as above
user.association(:child).scope.eager_load(:toy).first # makes the appropriate query with both child and toy... but doesn't set the object on the user model.
user.child = user.association(:child).scope.eager_load(:toy).first # makes the appropriate query, but also calls another query before setting the child :(
Is there any way to do this that doesn't involve re-querying the user model. ie. I want to avoid this
User.where(id: user.id).eager_load(child: :toy).first
Relavant model declarations:
class User < ActiveRecord::Base
has_one :child
has_one :toy, through: :child
end
class Child < ActiveRecord::Base
has_one :toy
belongs_to :user
end
class Toy < ActiveRecord::Base
belongs_to :child
end
Update
This works, but isn't ideal. I don't think I should have to declare another relation solely for this reason.
class User < ActiveRecord::Base
has_one :child
has_one :toy, through: :child
has_one :child_with_toy, ->{ eager_loads(:toy) }, class_name: "Child", foreign_key: :parent_id
end
which allows me to call user.child_with_toy to get the Child object, and user.child_with_toy.toy to get the Toy object, while only triggering one SQL query.
If you want to eager load an association of a singular association for an already-instantiated record, you can do this:
user.association(:child).target = user.association(:child).scope.eager_load(:toy).first
This is similar to one of the approaches you listed at the top of your question. In particular, notice the .target part.
ActiveRecord isn't optimized for this particular scenario, so the code is pretty ugly. For that reason, I would strongly lean toward your :child_with_toy approach if you really need to save the query.
It can be done easier with Preloader:
ActiveRecord::Associations::Preloader.new.preload(user, child: [:toy])
There is a cool feature for has_many in Rails. I can write
class Article < AR::Base
has_many :comments
has_one :another_association
and voila! method comment_ids= created, which I can use in strong parameters and mass assignment. Somethind like #article.comment_ids = [1,2,3]
I need something similar for has_one, like #article.another_association_id = 1. But I get NoMethodError exception. Is there any way to make this method works?
Has one has a different syntax.
#article.build_another_association
#article.create_another_association
See the guides for more info
Use attr_accessible
Specifies a white list of model attributes that can be set via mass-assignment
So you can add it like this assuming you created and ran the migration already:
class Article < AR::Base
has_many :comments
has_one :another_association
attr_accessible :another_association_id
But if it's Rails 4 you may need to handle it in the controller.
You have the direction of the association reversed.
With has_one, the other class should have an article_id to refer to a record in your articles table.
If you want articles.another_association_id, then you should specify belongs_to :another_association.
This method should only be used if the other class contains the foreign key. If the current class contains the foreign key, then you should use belongs_to instead.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_one
If you want to simulate the way has_many works you could try this:
class Article < AR::Base
has_one :page
def page_id=(int_id)
self.page = Page.find(int_id)
end
end
#article.page_id = 3
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
I am having models like
// Contains the details of Parties (Users)
class Party < ActiveRecord::Base
has_many :party_races
has_many :races, :through=>:party_races
end
// Contains the party_id and race_id mappings
class PartyRace < ActiveRecord::Base
belongs_to :party
belongs_to :race
end
// Contains list of races like Asian,American,etc..
class Race < ActiveRecord::Base
has_many :party_races
has_many :parties, :through => :party_races
end
Now, lets say I'm creating an instance of Party
party_instance = Party.new
How am I supposed to add multiple Races to party_instance and save to database ?
You could use nested attributes to make one form that allows children. There are many examples on this site. Read up on the following first:
Accepts nested attributes
Fields for
A railscast about your use case
You also can create new PartyRace for each Races that you can add:
def addRace( party_instance, new_race )
party_race = PartyRace.new( party: party_instance, race: new_race )
party_race.save
end
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..