Rails has_many through with condition, build new - ruby-on-rails

I've got users and organisations with a join model UsersOrganisation. Users may be admins of Organisations - if so the is_admin boolean is true.
If I set the is_admin boolean by hand in the database, Organisations.admins works as I'd expect.
In the console, I can do Organisation.first.users << User.first and it creates an organisations_users entry as I'd expect.
However if I do Organisation.first.admins << User.last it creates a normal user, not an admin, ie the is_admin boolean on the join table is not set correctly.
Is there a good way of doing this other than creating entries in the join table directly?
class User < ActiveRecord::Base
has_many :organisations_users
has_many :organisations, :through => :organisations_users
end
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_users, :class_name => "User",
:source => :user,
:conditions => {:organisations_users => {:is_admin => true}}
end
class OrganisationsUser < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
end

You can always override the << method of the association:
has_many :admins do
def <<(user)
user.is_admin = true
self << user
end
end
(Code has not been checked)

there are some twists with the has_many :through and the << operator. But you could overload it like in #Erez answer.
My approach to this is using scopes (I renamed OrganisationsUsers to Memberships):
class User < ActiveRecord::Base
has_many :memberships
has_many :organisations, :through => :memberships
end
class Organisation < ActiveRecord::Base
has_many :memberships
has_many :members, :through => :memberships, :class_name => 'User', :source => :user
# response to comment:
def admins
memberships.admin
end
end
class Memberships < ActiveRecord::Base
belongs_to :organisation
belongs_to :user
scope :admin, where(:is_admin => true)
end
Now I create new admins like this:
Organisation.first.memberships.admin.create(:user => User.first)
What I like about the scopes is that you define the "kind of memberships" in the membership class, and the organisation itself doesn't have to care about the kinds of memberships at all.
Update:
Now you can do
Organisation.first.admins.create(:user => User.first)

You can try below code for organization model.
class Organisation < ActiveRecord::Base
has_many :organisations_users
has_many :organisations_admins, :class_name => "OrganisationsUser", :conditions => { :is_admin => true }
has_many :users, :through => :organisations_users
has_many :admins, :through => :organisations_admins, :source => :user
end

Related

Adding objects with different association types in rails

The following are my three models: many users can each have many products (and vice versa) through an associations model.
class Product < ActiveRecord::Base
has_many :associations
has_many :users, :through => :associations
end
class User < ActiveRecord::Base
has_many :associations
has_many :products, :through => :associations
has_many :medium_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "medium"]
has_many :strong_associated_products, :class_name => "Product", :through => :associations, :source => :product, :conditions => ["associations.strength = ?", "strong"]
end
class Association < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
To add a "medium" association.strength product to the user, I usually do:
user.products << product #associations.strength is by default "medium"
My question is how would I do the same thing and add a product to the user but with "strong" association.strength initalized?
You can do the same with strong by
user.strong_associated_products << product
You may need to set to your relations this way:
class User < ActiveRecord::Base
has_many :medium_associations, class_name: 'Association', condition: ["associations.strength = ?", "medium"]
has_many :strong_associations, class_name: 'Association', condition: ["associations.strength = ?", "strong"]
has_many :medium_associated_products, class_name: 'Product', through: :medium_associations, source: :product
has_many :strong_associated_products, class_name: 'Product', through: :strong_associations, source: :product
end
Adding onto #sonnyhe2002's answer. I ended up using a callback such as
has_many :strong_associations, class_name: 'Association', condition: ["associations.strength = ?", "strong"], add_before: :set_the_strength
and then
def set_the_strength(obj)
obj[:strength] = "strong"
end

Rails - Help needed to create a New Model

class Person
has_many :owned_groups, :class_name => "Group", :foreign_key => :owner_id
has_many :owned_group_memberships, :through => :owned_groups,
:source => :group_memberships
has_many :group_memberships, :foreign_key => "member_id"
has_many :groups, :through => :group_memberships
end
class GroupMembership
belongs_to :member, :class_name => 'Person'
belongs_to :group
end
class Group
belongs_to :owner, :class_name => "Person"
has_many :group_memberships
has_many :members, :through => :group_memberships
end
I want to create the model Message so a Person can post a new message in the group#show page. But for that she/he must be from the Group in question, or the owner, or have a group_membership (be a member) from this group.
How would be the associations of the model Message?
class Message < ActiveRecord::Base
belongs_to :person
belongs_to :group
validate :has_group_permission
def has_group_permission
unless self.person.owned_groups.include?(self.group) || self.person.groups.include?(self.group)
self.errors.add(:base, "you don't have permission to add a message to this group."
end
end
end

multiple belongs_to relationship to three model

The situation is this way..
class Organization < ActiveRecord::Base
has_many :role_memberships
has_many :roles
has_many :users, :through => :role_memberships, :uniq => true
end
class User
has_many :role_memberships
has_many :organizations, :through => :role_memberships, :uniq => true
has_many :roles, :through => :role_memberships, :uniq => true
end
class RoleMembership < ActiveRecord::Base
belongs_to :organization
belongs_to :role
belongs_to :user
end
class Role < ActiveRecord::Base
belongs_to :organization
has_many :role_memberships
has_many :users, :through => :role_memberships, :uniq => true
end
The QUESTION is how do I populate all the three foreign-keys in rolemembership table..when I do org.users.push(u) this create a record but role_id is left out...
In this case I will probably create RoleMembership object itself, like this:
RoleMembership.create(:organization_id => org.id, :role_id => role.id, :user_id => user.id)

named_scope and HABTM association

I have a models User
class User < ActiveRecord::Base
has_many :ratings
has_many :rated_films, :through => :ratings, :source => :film
end
and Films
class Film < ActiveRecord::Base
has_many :users, :through => :ratings
end
I am looking to find all Films that have not been rated by the specified user, smth like
class Film < ActiveRecord::Base
has_many :users, :through => :ratings
named_scope :not_rated_by_user, lambda { |user|
{:joins => :users, :conditions => ['? NOT IN users', user]}
}
end
Film.not_rated_by_user(User.first)
I am not that familiar with SQL so am not quite sure if this could be achieved in a named scope.
Many thanks
Yuriy
I suppose you have a ratings table, which is your join table. Right? So you need something like:
class User < ActiveRecord::Base
has_many :ratings
has_many :rated_films, :through => :ratings, :source => :film
end
class Film < ActiveRecord::Base
has_many :ratings
has_many :users, :through => :ratings
named_scope :not_rated_by_user, lambda { |user_id| {
:include => :ratings,
:conditions => ['? NOT IN (ratings.user_id)', user_id]
}}
end
class Rating < ActiveRecord::Base
belongs_to :film
belongs_to :user
end
And you can use
Film.not_rated_by_user(User.first.id)
Please let me know if it helped. I haven't tested!

Nested Has Many Through Plugin and Named Scopes

I have a User Model(:name, :password, :email), and Event model(:name, :etc) and Interest model (:name) [>all singular<]
Then I created two join tables -> UsersInterests and EventsInterests; each not containing a primary key and only comprised of the user_id/interest_id and event_id/interest_id respectively. [>plural<]
My Models Use the Nested Has Many Through Plugin
user.rb => has_many :users_interests
has_many :interests, :through => :users_interests
has_many :events_interests, :through => :interests
has_many :events, :through => :events_interests
event.rb => has_many :events_interests
has_many :interests, :through => :events_interests
has_many :users_interests, :through => :interests
has_many :users, :through => :users_interests
interest.rb => has_and_belongs_to_many :users
has_and_belongs_to_many :events
events_interests.rb => belongs_to :interests
belongs_to :events
users_interests.rb => belongs_to :users
belongs_to :interests
Whew..ok So I wanted to created a named_scope of that find all the events that share interest with a particular user. Here is some code someone helped me with.
named_scope :shares_interest_with_users, lambda {|user|
{ :joins => :users_interests,
:conditions => {:users_interests => {:user_id => user}}
}}
When i run from the controller =>
#user = User.find(1)
#events = Event.shares_interest_with_user(#user)
I get the error :
uninitialized constant Event::EventsInterest
Can anyone see what i messed up?
You must have named something wrong along the way. At a glance I'd say you have a file or class named incorrectly. Remember model names MUST always be singular, both in file and class names or else Rails won't make the connection. Another source of your problem is that arguments to belongs_to must also be singular. Even if you had got things right, the HABTM relationship in interests with users would have thrown an error when you ran the named scope.
I was able to solve your error with the following models.
user.rb
class User < ActiveRecord::Base
has_many :users_interests
has_many :interests, :through => :users_interests
has_many :events_interests, :through => :interests
has_many :events, :through => :events_interests
end
users_interest.rb
class UsersInterest < ActiveRecord::Base
belongs_to :user
belongs_to :interest
end
interest.rb
class Interest < ActiveRecord::Base
has_many :users,:through => :users_interests
has_many :users_interests
has_many :events_interests
has_many :events, :through => :events_interests
end
**events_interest.rb
class EventsInterest <ActiveRecord::Base
belongs_to :interest
belongs_to :event
end
event.rb
class Event <ActiveRecord::Base
has_many :events_interests
has_many :interests, :through => :events_interests
has_many :users_interests, :through => :interests
has_many :users, :through => :users_interests
named_scope :shares_interest_with_users, lambda {|user|
{ :joins => :users_interests,
:conditions => {:users_interests => {:user_id => user}}
}
}
end

Resources