has_one - how to assign_id? - ruby-on-rails

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

Related

Rails 4 Accept nested attributes with has_one association

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)

Can I create a has_one association in a single call?

I have an Item which I split the table with cache_item. CachItem contains some serialized fragments. Like this:
class Item < ActiveRecord::Base
has_one :cache_item
end
class CacheItem < ActiveRecord::Base
belongs_to :item
end
how would I tell it to create one and save it automatically?
I do something like this:
if !cache_item
CacheItem.create! item_id: id
self.reload # seems like I shouldn't have to do this
end
but seems like there should be a single call. Is there?
For a has_many, I can do:
>item.comments.count
>1
>item.comments.create! # inserts with proper information
>2
What is the pattern for a has_one?
For a has_one association, you can use create_association! as documented: http://guides.rubyonrails.org/association_basics.html#has-one-association-reference
In your case
item.create_cached_item!
For an Item that has_many Comment(s) you use:
Item.comments.create()
And for an Item that has_one Comment you use:
Item.create_comment

How to get all the associated models from ActiveRecord object?

For example I have
class Order < ActiveRecord::Base
has_many :shippings
has_one :contact_information
belongs_to :shop
end
How to get an array of associated objects from Order. For example
Order.associations
# [:shipping, :contact_information, :shop]
Order.reflect_on_all_associations.map(&:class_name)
You can pass a type of relation as a parameter:
Order.reflect_on_all_associations(:has_one)
Read about ActiveRecord::Reflection::ClassMethods
EDIT
Just realised, you've asked about object's associated models.
So, having what I've already shown, you can simply do something along the following lines:
associated_models = Order.reflect_on_all_associations.map(&:class_name)

Struggles with has_many and belongs_to association when relationships are 2-3 deep

I'm pretty new to Rails and setting up associations, so I suspect I'm missing something pretty obvious. I'm trying to set up an app where one model has two models that it has_many of. The second model belongs_to the first and has_many of the third. And the third can either belong to the first or the second model.
Specifically, I have a wall model that holds pictures and collages. The wall can hold either pictures or collages or neither. Collages can hold pictures.
class Wall < ActiveRecord::Base
belongs_to :user
has_many :collages
has_many :pictures
end
class Collage < ActiveRecord::Base
belongs_to :user
belongs_to :wall
has_many :pictures
end
class Picture < ActiveRecord::Base
belongs_to :user
belongs_to :wall
belongs_to :collage
end
The error I'm getting is telling me:
undefined method `picture?' for #Wall
Is there something I'm doing wrong with the associations I'm creating?
has_many association on any model gives plural form of that method
Therefore Wall class has method #pictures available by this line:
If you want #picture method to be available you should use association as belongs_to
We can debug more into the exact problem if you tell where actually you are getting this error and what is your feature to implement.
Also name for Picture class should be with capital P
#cvibha's answer should help you with the associations
However, there's another problem you may need to consider. You're calling this method:
undefined method `picture?' for #Wall
Rails associations basically create a record as per how you define the association (has_many :pictures creates #wall.pictures). However, you're calling picture?
--
If you've got a custom method called picture?, this should work (albeit without the association working - as described in the other answer). The problem you have is I don't think you've defined picture?
I would do this:
#app/models/wall.rb
Class Wall < ActiveRecord::Base
...
def picture?
#your code
end
end
Alternatively, if you're looking to validate the existence of a picture, you may wish to use in your view:
#wall.pictures.any?
#wall.pictures.first.present?

How to check if object is referenced before deleting? (Many To Many Relationship)

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..

Resources