I'm not quite sure what to even search for to address this specific question.
So here goes...
My current design:
User < AR
has_many :items
has_many :projects
Project < AR
has_many :groups
Group < AR
has_many :items
Given a #project and a #user how do I get Groups for a project (#project.groups) but have those Groups only contain items for the given #user?
This seems like something that should be taken care of in a model, rather than as controller logic, but I'm not sure what the best Rails way for doing this would be. I've investigated scope and custom finders, but it seems unnecessarily complicated. Perhaps it's a flaw in the relationship between the models.
Edit: Perhaps this helps?
User < AR
has_many :items
has_and_belongs_to_many :projects
Project < AR
has_many :groups
has_and_belongs_to_many :users
Group < AR
has_many :items
I think you're looking for has_many :through:
class User < ActiveRecord::Base
has_many :projects
has_many :groups, :through => :projects
has_many :items, :through => :groups
end
class Project < ActiveRecord::Base
has_many :groups
end
class Group < ActiveRecord::Base
has_many :items
end
Then you can just call:
#user.items
to get all of the items for a given user.
Note that nested has_many :throughs only work since Rails 3.1; otherwise, you'll probably have to write a method!
This has a little raw SQL, but should work:
def Project
def groups_for_user u
self.groups.select("DISTINCT groups.*").join(:items).where("items.user_id = ?,u.id)
end
end
The reason for the "DISTINCT groups.*" is to prevent repeats in the result because you're joining the lower level items table.
Related
I have two models with a relation like this:
class Ticket < ActiveRecord::Base
belongs_to :group
end
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
has_many :tickets, as: :assignable
end
class Group < ActiveRecord::Base
has_many :tickets, -> { order(:created_at) }
has_and_belongs_to_many :users
end
I need to get all tickets belonging to the same groups the user has.
How can I accomplish that? Thank you so much!
As things stand, your relations are incomplete and so Rails won't work properly. If User has_many Tickets then Tickets must belong_to (or at least has_one) User. Alternatively, User can have_many Tickets through Group, which seems more likely in this case.
However, even then, it's not clear what your Group model is doing. Particularly, it's not clear how you intend it to relate to User - this looks like quite a complex relationship.
To start with, though, try and set the models up like this:
class Ticket < ApplicationRecord
belongs_to :group
end
class Group < ApplicationRecord
belongs_to :user
has_many :tickets, dependent: :destroy
end
class User < ApplicationRecord
has_many :groups, dependent: :destroy
has_many :tickets, through: :groups
end
(You'll see that I've also inherited these models from ApplicationRecord, which is how I've always done it.)
If you set it up as above, you can get your ticket records with a simple #user.tickets.
If this works, you can then add the extra HABTM relationship for Groups and Users. But be aware that HABTM relationships can be complex and there are good and bad ways to use them.
(If the primary relationship you really want is Groups > Users > Tickets then let me know and I can adjust accordingly.)
I have the following models:
class User < ActiveRecord::Base
has_many :forum_users
has_many :forums through: :forum_users
end
class Forum < ActiveRecord::Base
has_many :forum_users
has_many :users through: :forum_users
end
class ForumUser < ActiveRecord::Base
belongs_to :forums
belongs_to :users
end
ForumUser has an additional "permissions" field to designate what the user can and cannot do within a particular forum.
The problem is that the "permissions" field on ForumUser is much more difficult to access than fields on Rails models normally are. In an ideal world, I would be able to make requests like:
user = forum.users.first
user.permissions
But once it's assigned, the User doesn't even remember the Forum it was attained through in the first place. It seems like there has to be a simple and straightforward Rails way of doing this. If so, what is it?
I have this relationship between categories, products & brands:
class Brand < ActiveRecord::Base
has_many :products
end
class Category < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :categories
belongs_to :brand
end
How can I select all categories by specified brand with this relations?
I try this but get an error
b = Brand.find(1)
Category.joins(:products).where(:products => b.products)
You did the right thing with the join, just add a more complex where definition:
Category.joins(:products).where(:products => {:brand_id => 1})
Controversially HABTM's are rarely, if ever, a good design and IMO just about the only thing Rails got wrong.
Introduce an xref table to join products and categories and use has_many :through on both sides of the relationship so you end up with
class Brand < ActiveRecord::Base
has_many :products
has_many categories :through => products # This is now allowed in Rails 3.x and above
end
class Category < ActiveRecord::Base
belongs_to :product_category
has_many :products :through => product_category
end
class Product < ActiveRecord::Base
belongs_to :brand
belongs_to :product_category
has_many :categories :through => product_category
end
class ProductCategory < ActiveRecord::Base
has_many :products
has_many :categories
end
This gives you the best flexibility with the least amount of code re-factoring for you plus a much more intuitive path to get whatever data you need on either side of the relationship and will enable you to achieve the following
b = Brand.find(1)
b.categories.all
Update
The above is totally untested code and I have just corrected a glaringly stupid mistake I made. If you have any issues implementing this then come back
I have a User model
class User < ActiveRecord::Base
has_many :projects
end
and I have a Project model
class Project < ActiveRecord::Base
belongs_to :user
end
Obviously right now each project is owned by a user and there can only be one user per project. I now want to make my models represent another relation between the two models. I want a User to be able to follow multiple Projects, no matter who owns the Project. I know that I am going to have to use a has_many :through and create a join, but I cant wrap my head around how to change the model to keep my current relationship and add the new relationship.
Well, in that case, in your show/index action display all the projects (Project.all) in your project table. This way all users have access to all the projects. Now, in your edit action, use user.projects.all to display projects of that particular user. That should solve your problem, I don't see the need of any further association here.
Update:
This should suffice:
class Project < ActiveRecord::Base
belongs_to :user
class User < ActiveRecord::Base
has_many :projects_followed, :through => :projects
user has_many :projects_owned, :through => :projects
If you don't wish to create two more relations, create just one:class ProjectsSubscribed
belongs_to :project with three fields: project_id, is_owned, is_followed
Try following relation.
class User < ActiveRecord::Base
has_many :followers
has_many :projects, :through => :followers
end
class Follower < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
class Project < ActiveRecord::Base
has_many :followers
has_many :users, :through => :followers
end
Note it:
You can use has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship.
Hope it is helpful.
I have a situation where I have Products, Suppliers, ShoppingLists and Valuations.
A shopping_list consist of many valuations each with a product, an specific supplier and a price.
My models are as follows:
class Product < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class Supplier < ActiveRecord::Base
has_many :valuations
has_many :shopping_lists, :through => :valuations
end
class ShoppingList < ActiveRecord::Base
has_many :valuations
has_many :products, :through => :valuations
has_many :suppliers, :through => :valuations
end
class Valuation < ActiveRecord::Base
belongs_to :product
belongs_to :supplier
belongs_to :shopping_list
end
My routes.rb is:
map.resources :shopping_lists do |shopping_list|
shopping_list.resources :valuations
end
map.resources :product
map.resources :supplier
I wonder if this could be the best solution, anyway what I want is that the user can create as many lists as he wish, each with several valuations.
The first time a shopping list is created its also filled with one valuation at least. Then, the user can add/remove valuations to/from the shopping_list.
I would like a simple and elegant solution, without Ajax callbacks.
What is the best way to do this, from the controllers/views/routes perspectives?
Or should I completley change my schema ?
Thanks!
Just found two excelent resources from Ryan Bates:
http://asciicasts.com/episodes/196-nested-model-form-part-1
http://asciicasts.com/episodes/197-nested-model-form-part-2
Let's see if that do the job!
// UPDATE: Worked great!