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
Related
I have a model with associations. How to create/update the associations as CRUD operations are performed on the model.
That is, when I run
#v1_seller = V1::Seller.new(seller_params)
#v1_seller.save
It should save the associations.
Should I create after_create hooks and pass the params (but then I will have to do the same in update)? Or am I missing something? I feel that it should be done automatically in rails.
currently I am doing it explicitly:
#v1_seller = V1::Seller.new(seller_params)
if #v1_seller.save
#v1_seller.assign_categories(params)
my seller model:
class V1::Seller < ActiveRecord::Base
has_many :categories, :class_name => 'V1::Category', dependent: :delete_all
has_many :category_names, :class_name => 'V1::CategoryName', through: :categories
# right now I am manually calling this after a create/update operation in my controller
def assign_categories(params)
params.require(:seller).require(:categories)
params.require(:seller).permit(:categories => []).permit(:name, :brands => [])
self.categories.clear
params[:seller][:categories].each do |c|
if c[:brands].nil? || c[:brands].empty?
next # skip the category if it has no brands associated with it
end
category_name = c[:name]
category = V1::Category.new
category.category_name = V1::CategoryName.find_by(name: category_name)
category.seller = self
category.save
c[:brands].each do |b|
begin
category.brand_names << V1::BrandName.find_by(name: b)
rescue ActiveRecord::RecordInvalid
# skip it. May happen if brand is already added to the particular category
end
end
end
end
end
And V1::Cateogry model:
class V1::Category < ActiveRecord::Base
belongs_to :category_name, :class_name => 'V1::CategoryName', inverse_of: :category
belongs_to :seller, :class_name => 'V1::Seller', inverse_of: :category
has_many :brands, :class_name => 'V1::Brand', dependent: :delete_all, inverse_of: :category
has_many :brand_names, :class_name => 'V1::BrandName', through: :brands, inverse_of: :category
validates :seller, :uniqueness => {:scope => [:category_name, :seller]}
end
Seem like you need nested attributes.
Checkout the docs here: http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
I have these models that obviously have more attributes, but for simplicity I kept them just like this:
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "subs"
end
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :user
end
In the table subscriptions, there's a column called email. This column points to the table users where matches an email address of a single user (the column email is in both tables unique).
I would need to create an association between these two models based on the email value. But when I try to run this query (and to get all subscription for the currently sign in user):
<%= current_user.subs.inspect %>
I get this error message:
uninitialized constant User::subs
I'd like to ask you guys for helping me with this association.
Thanks
uninitialized constant User::subs
This code
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "subs"
end
should be like this
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "Subscription"
end
When you are using a class_name option with the associations,it should point to the respected model classname(in your case it is Subscription not subs).Since there is no model with the classname subs,it throws that exception.
class User < ActiveRecord::Base
has_many :subs, :foreign_key => :email, :class_name => "Subscription", :primary_key => :email
end
class Subscription < ActiveRecord::Base
belongs_to :plan
belongs_to :user, :foreign_key => :email, :class_name => "Subscription", :primary_key => :email
end
You have to set the correct Class name and also you have to set the primary key.
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.
I have my model defined like this:
class Animal < ActiveRecord::Base
mount_uploader :picture, PictureUploader
attr_accessible :picture, :born_date, :father_id, :mother_id, :name, :obs, :earring, :animal_type, :animal_type_id, :inseminations
validates :name, :born_date, :presence => true
validates :earring, :presence => true, :if => :should_have_earring?
belongs_to :father, :class_name => "Animal"
belongs_to :mother, :class_name => "Animal"
belongs_to :animal_type
has_one :birth
has_one :sell
has_one :death
has_many :inseminations
end
and
class Insemination < ActiveRecord::Base
attr_accessible :bull_id, :cow_id, :done, :expected_birth_date, :expected_dry_date, :insemination_date, :obs, :rut
validates :bull_id, :presence => true
validates :cow_id, :presence => true
validates :insemination_date, :presence => true
belongs_to :bull, :class_name => "Animal"
belongs_to :cow, :class_name => "Animal"
has_one :birth
has_one :abortion
has_one :dry
end
Good, somewhere, I want to get the last insemination from some animal... so, I do #animal.inseminations.last, it should work, but, it does the select using a animal_id property, that does not exist in insemination model. So I get an error like this:
Mysql2::Error: Unknown column 'inseminations.animal_id' in 'where
clause': SELECT inseminations.* FROM inseminations WHERE
inseminations.animal_id = 1 ORDER BY inseminations.id DESC
LIMIT 1
How can I specify it to searh in cow_id and/or bull_id columns? Is that possible?
Thanks in advance.
I think you have a few different options:
1) Instead of using has_many :inseminations, create two separate has many relationships:
has_many :cow_inseminations, :class_name => 'Insemination', :foreign_key => 'cow_id'
has_many :bull_inseminations, :class_name => 'Insemination', :foreign_key => 'bull_id'
2) Use STI and create subclasses of Animal. You will need to add a type field to Animal for this to work:
class Cow < Animal
has_many :inseminations, :foreign_key => 'cow_id'
end
class Bull < Animal
has_many :inseminations, :foreign_key => 'bull_id'
end
Then you can do Bull.first.inseminations or Cow.first.inseminations
You can specify a foreign key:
has_many :inseminations, :foreign_key => :bull_id
However you can only have one foreign_key, so it doesn't work for the cows.
You can do something like Rails Model has_many with multiple foreign_keys to get it to work. But then in this case, you need:
has_many :bull_inseminations, :foreign_key => :bull_id
has_many :cow_inseminations, :foreign_key => :cow_id
def inseminations
# not sure how you store animal type, but something like
return bull_inseminations if animal_type == "bull"
return cow_inseminations if animal_type == "cow"
end
For other attribute methods, you will need to do something similar if you want to use them, eg:
def inseminations_changed?
bull_inseminations_changed? or cow_inseminations_changed?
end
and similarly with inseminations<<, inseminations=, etc.
So I have two ActiveRecord classes
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name # THIS SHOULD ONLY BE RUN IF USER IS A SELLER
# IN THE DEAL
validates_presence_of :phone # THIS SHOULD ONLY BE RUN IF USER IS A BUYER
# IN THE DEAL
end
class Deal < ActiveRecord::Base
belongs_to :seller, :class_name => 'User'
belongs_to :buyer, :class_name => 'User'
validates_associated :seller
validates_associated :buyer
end
What I want to do is create a new deal with.
Deal.create(A NICE STRUCT WITH SELLER AND BUYER)
However I only want to run the name validation if the relation from the deal is a seller and the phone if the the relation from the deal is a seller, is this possible in rails, does not seem to find anything in the documentation.
You should be able to do this by adding a condition to you validation.
So, your User class would wind up looking something like...
class User < ActiveRecord::Base
has_many :buyer_deals, :class_name => "Deal", :foreign_key => :buyer_id
has_many :seller_deals, :class_name => "Deal", :foreign_key => :seller_id
validates_presence_of :name, :if => :has_an_active_seller_deal?
validates_presence_of :phone, :if => :has_an_active_buyer_deal?
def has_active_seller_deals?
seller_deals.count > 0
end
def has_active_buyer_deals?
buyer_deals.count > 0
end
end
An alternative to this would be to simply require all users to have a name and phone number on file at all times (no conditional validation), and only reveal it to other users with which they had active deals, and not as part of a user's public profile, thereby protecting the user's privacy when possible. This would probably be simpler.
You could put the validations in a callback:
before_save :check_user_type
private
def check_user_type
user_type = self.responds_to?(seller_id) ? :seller : :buyer
if user_type == :seller
validates_presence_of :name
else
validates_presence_of :phone
end