I have 3 different Models
class GroupMember < ActiveRecord::Base
attr_accessible :group_id, :user_id, :owner_id
has_one :user
end
class Group < ActiveRecord::Base
attr_accessible :name, :owner, :permission
has_many :groupMembers
end
class User < ActiveRecord::Base
end
and when im in the groups_controller.rb i want realize the following query with associations
SELECT * FROM groups
LEFT JOIN group_members ON groups.id = group_members.group_id
LEFT JOIN users ON group_members.user_id = users.id WHERE users.id = 1
or the joins query
Group.joins('LEFT JOIN group_members ON groups.id = group_members.group_id LEFT JOIN users ON group_members.user_id = users.id').where('users.id = ?', current_user.id );
Is this possible?
Group.joins(:user_id => current_user.id)
I believe it is possible but it is a bit difficult to test as soon as I don't have a DB with these tables. Probably something like:
Groups.joins(:group_members, :user).merge(User.where(:id => current_user.id))
But the design is a bit strange. I suppose you want to fetch all groups for a certain user, and it is best put like this:
#user = User.find_by_id(current_user.id)
#groups = #user.groups
for this to work you should have groups association in your User model.
It is also unclear for me why you have GroupMember has_one :user. I understand that group member is related to only one user, but can a user be represented as several group members, of different groups? If yes, the Rails way to design it would be:
class GroupMember < ActiveRecord::Base
attr_accessible :group_id, :user_id, :owner_id
belongs_to :user
belongs_to :group
end
class Group < ActiveRecord::Base
attr_accessible :name, :owner, :permission
has_many :group_members
has_many :users, :through => :group_members
end
class User < ActiveRecord::Base
has_many :group_members
has_many :groups, :through => :group_members
end
Related
I have the following polymorphic association...
class Activity < ActiveRecord::Base
belongs_to :owner, polymorphic: true
end
class User < ActiveRecord::Base
has_many :activities, as: :owner
end
class Manager < ActiveRecord::Base
has_many :activities, as: :owner
end
I am trying to make a query whereby it only pulls out the activities where the owner (user or manager) has visible set to true.
I have figured out that if I want to do this for one of the owners, I can do this as follows...
Activity.joins("INNER JOIN users ON activities.owner_id = users.id").where(:activities => {:owner_type => 'User'}).where(:users => {:visible => true})
But I cannot figure out how to do it for both. Can anyone help?
This should work:
Activity.
joins("LEFT JOIN users ON activities.owner_type = 'User' AND
activities.owner_id = users.id").
joins("LEFT JOIN managers ON activities.owner_type = 'Manager' AND
activities.owner_id = managers.id").
where("users.visible = ? OR managers.visible = ?", true, true)
I have two models, Group and User. A user can create many groups and is then the owner of those groups. Other users can join those groups and therefore become members of those groups.
My question now is how to model this with Ruby on Rails.
I have created a join table between Group and Users and I added a reference from Group to the owner (foreign id to user).
Note: not sure if I did all this correctly.
Here are the attributes I currently have in Group and User (I don't know how to access the join table, so I can't show it to you).
Group: id, topic, home_town, created_at, updated_at, user_id
user_id was added with the following migration and I want it to reference the owner:
class AddOwnerReferenceToGroup < ActiveRecord::Migration
def change
add_reference :groups, :user, index: true
end
end
User: id, name, email, password_digest, remember_token, created_at, updated_at
As for the relationships, here's what each class contains:
User -> has_many :groups
Group -> has_many :users
Is it possible (and do I have) to add a belongs_to relationship in the group class to reference to the owner?
I would set it up like so:
class User < ActiveRecord::Base
has_many :group_users, :dependent => :destroy
has_many :groups, :through => :group_users
has_many :owned_groups, :through => :group_users, :class_name => "Group", :conditions => ["group_users.owner = ?", true]
...
end
class Group < ActiveRecord::Base
has_many :group_users, :dependent => :destroy
has_many :users, :through => :group_users
def owner
self.users.find(:first, :conditions => ["group_users.owner = ?", true])
end
def owner=(user)
if gu = self.group_users.find_by_user_id(user.id)
gu.update_attributes(:owner => true)
else
self.group_users.create(:user_id => user, :owner => true)
end
end
...
end
#group_users table has fields user_id, group_id, owner(bool)
class GroupUser < ActiveRecord::Base
belongs_to :group
belongs_to :user
after_save :enforce_single_owner
def enforce_single_owner
if self.changes["owner"] && self.owner
GroupUser.find(:all, :conditions => ["group_id = ? and id <> ? and owner = ?", self.group_id, self.id, true]).each{|gu| gu.update_attributes(:owner => false)
end
end
...
end
In this schema, the join table model has responsibility for tracking which of the members of the group is the owner of the group. A group has many users, and one of those wil be the owner.
Yes, it is possible and you should.
By adding to your Group
belongs_to :owner, class_name: 'User'
you can model your requirement that a group is created by one of the users.
In my snippet I used an arbitrary name in belongs_to just to make the code more readable. You don't have to but it definitely clears up things. The downside is that ActiveRecord cannot guess to which class you refer when using custom association names, thus, you need to manually specify it.
In a similar fashion you could change has_many :users to
has_many :members, class_name: 'User'
Whatever feels more natural when you read your code.
I have many-to-many relationship between Game and Account models like below:
class Account < ActiveRecord::Base
has_many :account_games, :dependent => :destroy
has_many :games, :through => :account_games
end
class Game < ActiveRecord::Base
has_many :account_games, :dependent => :destroy
has_many :accounts, :through => :account_games
end
class AccountGame < ActiveRecord::Base
belongs_to :account
belongs_to :game
end
Now I know let's say I want to create a record something like:
#account = Account.new(params[:user])
#account.games << Game.first
#account.save
But how am I supposed to update some of the attributes in AccountGame while I'm doing that? Lets say that AccountGame has some field called score, how would I update this attribute? Can you tell me about the best way to do that? To add any field in the through table while I'm saving the object.
#account = Account.new(params[:user])
#accountgame = #account.account_games.build(:game => Game.first, :score => 100)
#accountgame.save
Though I'd strongly recommend that if you start adding columns to your join-model that you call it something different eg "subscription" or "membership" or something similar. Once you add columns it stops being a join model and starts just being a regular model.
This should work:
class AccountGame < ActiveRecord::Base
belongs_to :account
belongs_to :game
attr_accessible :account_id, :game_id #<======= Notice this line
end
Models:
class User < ActiveRecord::Base
has_many :attendances
has_many :courses, :through => :attendances
end
class Course < ActiveRecord::Base
has_many :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :user
belongs_to :course
end
Migrations:
create_table(:users) do |t|
t.string :name
end
create_table(:courses) do |t|
t.string :name
end
create_table(:attendances) do |t|
t.references :user, :course
t.date :date
end
Question
I would like to query:
a list of all courses
the date of a given user's last attendance (if any) for each course
What is the best way to tie the following together into a single query?
#courses = Course.all
#user = User.first
#attendances = #user.attendances.group('course_id').order('date DESC')
Note that there is a requirement to include courses that a user has not yet attended.
Any advice much appreciated.
Update
The result I am looking for is as follows:
Course Last attended
===============================
Some course 2011-03-09
More training Not Attended
Another course 2010-12-25
In SQL, I would write this query as:
SELECT * FROM courses AS c
LEFT OUTER JOIN attendances AS a ON a.course_id=c.id
WHERE a.user_id=1
GROUP BY a.course_id
ORDER BY a.date DESC;
I could always execute this SQL from within Rails, but I would prefer to avoid this and do things "the Rails way" if possible.
The "Rails-Way" is to define small finders in the model and then to chain them in the controllers.
In class Attendance you could define a new method
def self.last_attendance
maximum("date")
end
In class Corse ...
def self.ordered
order("name DESC")
end
And so on. In the controller then, you use them in different combinations. The big advantage of this approach is
Reusability of finder methods
Decoupling of classes
Better readability of code
Your has_many through relation should be like this:
class User < ActiveRecord::Base
has_many :attendances
has_many :courses, :through => :attendances
end
class Course < ActiveRecord::Base
has_many :attendances
has_many :users, :through => :attendances
end
class Attendance < ActiveRecord::Base
belongs_to :user
belongs_to :course
end
And please explain your problem. Because
#user.courses will give you only related courses
Course.all will give you all courses
Edited:
Course.find(:all, :joins => 'LEFT OUTER JOIN attendances ON attendances.course_id =
courses.id', :conditions => ['attendances.user_id = ?', 1], :group =>
'attendances.course_id', :order => 'attendances.date DESC')
I hope it will work for you.
Right now I'm building a social media app, where i want an user to have a rating per category, how would the association go? The way it needs to be setup it's Each user will have a different rating in each category.
I'm think that
belongs_to :user
belongs_to :category
in the UserCategoryRating model.
and
has_many :user_category_ratings, through => :category
on the User model, Is this the correct approach?
The UserCategoryRating table has the User_id column, Category_id column, and the rating column, that updates each time an user gets votes (The rating it's just the AVG between votes and the score based on 1-5)
UPDATE: If I'm understanding you correctly, here is a diagram of the simple design you'd like:
And this would be the basic skeleton of your classes:
class User < ActiveRecord::Base
has_many :ratings
# has_many :categories, :through => :ratings
end
class Category < ActiveRecord::Base
has_many :ratings
# has_many :users, :through => :ratings
end
class Rating < ActiveRecord::Base
belongs_to :user
belongs_to :category
validates_uniqueness_of :user_id, :scope => [:category_id]
end
Will allow for these query:
#category_ratings_by_user = Rating.where("ratings.user_id = ? AND ratings.category_id = ?", user_id, category_id)
#specific_rating = user.ratings.where("ratings.category_id = ?", category_id)
# make nice model methods, you know the deal
# ... if you added the has_many :through,
#john = User.find_by_name("john")
# Two ways to collect all categories that john's ratings belong to:
#johns_categories_1 = #john.ratings.collect { |rating| rating.category }
#johns_categories_2 = #john.categories
#categories_john_likes = #john.categories.where("categories.rating >= ?", 7)
I'm just unsure as to why you want this has_many, :through (this doesn't seem like a many to many -- a rating only belongs to one user, correct?).
I will use the following data model:
class User
has_many :user_categories
has_many :categories, :through => :user_categories
end
class UserCategory
belongs_to :user
belongs_to :category
# this model stores the average score also.
end
class Category
has_many :user_categories
has_many :users, :through => :user_categories
end
Now when you want to update the score of a user for a category
uc = u.user_categories.find_by_category_id(id)
uc.score = score
uc.save