Received ActiveRecord Relation, but need object - ruby-on-rails

i've written the following classes:
class Occupation < ActiveRecord::Base
has_many :pref_labels
has_many :cv_occupations
has_many :cvs, :through => :cv_occupations
validates_uniqueness_of :uri
# This function will return the specified label for the given language.
def label(language = Language.find_or_create_by_code(:en))
self.pref_labels.where("language_id = #{language.id}")
end
end
class PrefLabel < ActiveRecord::Base
belongs_to :language
belongs_to :concept
belongs_to :skill
belongs_to :occupation
validates_uniqueness_of :value, :scope => [:language_id, :value]
validates_uniqueness_of :language_id, :scope => [:language_id, :value]
end
In my view, I call the following: %td= occupation.label(#language)
but this returns as error :
undefined method `value' for #<ActiveRecord::Relation:0x80c8368>
How can I get the actuall object returned instead of the relation? I know this has something to do with the lazy loading....

Change
self.pref_labels.where("language_id = #{language.id}")
To
self.pref_labels.where("language_id = #{language.id}").all #or .first if you only one the first one

Related

Filtering Association By Another Association

I have a friendship model that contains a status.
class Friendship < ActiveRecord::Base
attr_accessible :friend_id, :user_id, :source_id
after_create :check_friend_status
# Relationships
belongs_to :user, :touch => true
belongs_to :friend, :class_name => "User", :touch => true
belongs_to :source
has_one :status, :class_name => "FriendStatusDescriptor", :foreign_key => 'friendship_id'
validates_uniqueness_of :user_id, :scope => [:friend_id, :source_id]
def check_friend_status
# Check user/friend for existing friend status
if FriendStatusDescriptor.find(:first, :conditions => ["friendship_id = ?", self.id]).nil?
status = FriendStatusDescriptor.new
status.friendship_id = self.id
status.save
end
end
end
class FriendStatusDescriptor < ActiveRecord::Base
attr_accessible :alert, :friendship_id, :hide
belongs_to :friendship
validates_uniqueness_of :friendship_id
end
The status model has a boolean variable called hide. I want to be able to filter my user's friendships by the ones with hide set to false. Something along these lines.
# In User Model
# Friendships
has_many :friendships do
def visible
# Where !friendship.status.hide
end
end
So that in my controller I can just do this
user.friendships.visible
I'm not sure how to access the individual friendship in this method though.
I think you want:
class User
has_many :friendships,
:class_name => "FriendStatusDescriptor",
:foreign_key => 'friendship_id'
If you want to filter visible and non-visible friendships by separete you can add scopes to Friendship model:
class Friendship
scope :visible, -> { joins(:status).where("friend_status_descriptors.hide = ?", false) }
Then apply that scope:
user.friendships.visible

Foreign Keys pointing to same Model

I have an User model
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :client_workouts
end
And a ClientWorkout model
class ClientWorkout < ActiveRecord::Base
attr_accessible :client_id, :trainer_id, :workout_id
belongs_to :client, :class_name => User, :foreign_key => 'client_id'
belongs_to :trainer, :class_name => User, :foreign_key => 'trainer_id'
end
I first want to know what or if I'm doing something wrong when writing the associations. Ideally I want to be able to call a query where I find the user's clients workouts where the user's id matches with client_id or trainer_id. So...
user.client_workouts.trainer???
This will not work as rails assumes that the ClientWorkout have a user_id column. I don't think there is any way to make a has_many relation that matches two columns... Instead you could create a method like this:
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :client_workouts, :foreign_key => "client_id"
has_many :trainer_workouts, :foreign_key => "trainer_id"
def client_and_trainer_workouts
ClientWorkouts.where("client_id = ? OR trainer_id = ?", id, id)
end
end
Otherwise you could create a scope on the ClientWorkout model like this:
class ClientWorkout < ActiveRecord::Base
attr_accessible :client_id, :trainer_id, :workout_id
belongs_to :client, :class_name => User, :foreign_key => 'client_id'
belongs_to :trainer, :class_name => User, :foreign_key => 'trainer_id'
scope :workouts_for_user,
lambda {|user| where("client_id = ? OR trainer_id = ?", user.id, user.id) }
end
You could also do both, and let the method on the use call the scope on the ClientWorkout.

NoMethodError when using .where (eager fetching)

I have the following model classes...
class Image < ActiveRecord::Base
attr_accessible :description, :title
has_many :imageTags
has_many :tags, :through => :imageTags
end
class Tag < ActiveRecord::Base
attr_accessible :name
has_many :imageTags
has_many :images, :through => :imageTags
end
class ImageTag < ActiveRecord::Base
attr_accessible :position
belongs_to :image
belongs_to :tag
end
And when I use find for getting the Tag with the id 1
t = Tag.find(1);
#images = t.images;
But when I do the same with where, I get a NoMethodError, with the description undefined method 'images':
t = Tag.where(:name => "foo");
#images = t.images;
I also tried adding .includes(:images) before the .where statement, but that doesn't work too. So, how can I get all Images that belong to a Tag?
.where returns an ActiveRecord::Relation instance, not a single object. Tack on a .first to grab (presumably) the only record returned:
t = Tag.where(name: "foo").first
#images = t.images

how to create by default an instance of an object in a has_one relationship

Is there a DSL for creating an object in an AR relationship that would be the opposite of :dependent => destroy (in other words create an object so that it always exists). Say, for example, I have the following:
class Item < ActiveRecord::Base
#price
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
....
class Price < ActiveRecord::Base
belongs_to :pricable, :polymorphic => true
attr_accessible :price, :price_comment
I'm thinking I'd like for a price to be created every time even if we don't specify a price? Is the only (or best) option to do this as a callback or is there a way to do this via a DSL (similar to :denpendent => :destroy)?
No, as there is virtually no use-case for this. If your record cannot exist without an associated record, you should probably be preventing the record from being saved, not shoe-horning in some kind of pseudo-null object to take its place.
The closest approximation would be a before_save callback:
class Item < ActiveRecord::Base
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
before_save :create_default_price
def create_default_price
self.price ||= create_price
end
end
You should only run this code one time on create and use the convenience method create_price here:
class Item < ActiveRecord::Base
has_one :price, :as => :pricable, :dependent => :destroy
accepts_nested_attributes_for :price
after_validation :create_default_price, :on => :create
def create_default_price
self.create_price
end
end

Adding many models to another model

I'm currently working on a small project using Ruby On Rails 3.2 to create a database that contains several unique Models. Each Model has many Elements and each Element has the potential to belong to many Models. I have been able to set up the models in the following manner:
class Model < ActiveRecord::Base
has_many :model_elements
has_many :elements, :through => :model_elements
attr_accessible :elements, :name, :notes, :ref
end
class Element < ActiveRecord::Base
has_many :model_elements
has_many :models, :through => :model_elements
attr_accessible :elementType, :name, :notes, :ref
validates_presence_of :name
end
class ModelElement < ActiveRecord::Base
belongs_to :Model
belongs_to :element
attr_accessible :model_id, :created_at, :element_id
end
My question is how do I add multiple Elements to a single Model? I've tried to find some documentation but I can't find anything. Currently I'm trying to do the following:
#model.elements = #element
Where #element is a predefined element however it's throwing the following error:
undefined method `each' for #<Element:0x007ff803066500>
Any help would be greatly appreciated.
Try
#model.elements << #element
collection.create(attributes = {})
Returns a new object of the collection type that has been instantiated with attributes, linked to this object through the join table, and that has already been saved.
#model.elements.create(:name => "example")
Amar's answer is correct. If you wanted you can simplify your models further by using the has_and_belongs_to_many association.
class Model < ActiveRecord::Base
has_and_belongs_to_many :elements, :join_table => :model_elements
end
class Element < ActiveRecord::Base
has_and_belongs_to_many :models, :join_table => :model_elements
end
#model.elements << #element

Resources