Alright, a Rails Noob here, :D
It looks like has__many :through is the latest greatest way to handle many to many relationships, but I am trying to keep this simple. Hopefully one of you guru's out there have handled this situation before:
Here is the basic model setup I have now:
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :clients
end
class Client < ActiveRecord::Base
has_and_belongs_to_many :products
end
Essentially, I have users in the system, that will have access (through association) to many different products that are being created, those products have many clients, but the clients can also be a part of many products, and the Products accessed by many users.
All of the associations are working well, but now I want users to be able to add clients to their products, but only see clients that are associated with products they have access too.
Scenario:
Given Bob has access to product A and B
And does NOT have access to product C
And and has clients on product B
And wants to add them to product A.
When in product A Bob should see clients from product B in his add list,
And Bob should not see clients from product C
My noobish experience with rails fails to give me the experience on how to best build the array that will hold his client list.
The way I am thinking be to use #bob.products to get the products Bob has access to then to .each those and find the clients associated with each product and then join them into a single array. But is this the BEST way?
Thanks!
Not sure if this is what you're looking for, but if you want to remove all non-authorized clients for a particular user:
user = current_user
#clients_access = Array.new
user.products.each { |p| #clients_access.push(p.clients).uniq! }
#clients_access.flatten!
Alright so I achieved the functionality I wanted by the following:
user = current_user
#clients_no_access = Client.find(:all, :order => :business_name)
user.products.each do |product|
#clients_no_access -= product.clients
end
#all_clients = Client.find(:all,
:order => :business_name) - #clients_no_access - #product.clients
Basically, finding all the the clients, then iterating through the linked authorized products and removing them from the list, basically creating a list of non-authorized clients.. then doing the search again and clearing out the non-authorized clients and the clients already assigned in the group.. But, I have ran out of duct-tape.. any better solutions?
Related
So I have been trying to create a dummy application to try and learn Rails. The app I thought I could create is a coffee ordering app for a group of people in work.
So the website will have many users.
A user can create a coffee_order.
A coffee order contains orders for other individual users.
Each user can have one or more coffee_shop_items (e.g. latte,
cappuccino,danish, muffin, etc)
A coffee order also has an assignee, this is the person who is tasked
with going and getting the order.
So as a user, I create a coffee order, select an assignee, add users to the order, and add one or more coffee shop items to each user,
I am really struggling with how the database should be, and what the associations need to be, along with any join tables?
I am also trying to use nested attributes for the form entry.
Thanks in advance for help.
Update with some code I have tried to create a coffee order:
#coffee_order = CoffeeOrder.new(coffee_order_params)
params[:coffee_order][:user_coffee_orders_attributes].each do |user_order|
order = #coffee_order.user_coffee_orders.new(user_id: user_order[1][:user_id].to_i)
user_order[1][:coffee_shop_items].each do |item|
coffee_shop_item = CoffeeShopItems.find(item) if item != ""
# this line fails! see error below
#coffee_order.user_coffee_orders.coffee_shop_items << coffee_shop_item if coffee_shop_item != nil
end
end
error:
NoMethodError (undefined method `coffee_shop_items' for #<UserCoffeeOrder::ActiveRecord_Associations_CollectionProxy:0x42c6180>):
The coffee_shop_items belong to the order, not the user. After all, a user could probably create another order another day? You should probably also check out the rails documentation, which, IIRC actually contains a walk-through of a shopping cart application.
User has_many :coffes_orders
User has_many :coffee_orders_he_needs_to_get, class_name: "CoffeeOrder", foreign_key: "assignee_id"
CoffeeOrder belongs_to :user
CoffeeOrder belongs_to :assignee, class_name: "User"
CoffeeOrder has_and_belongs_to_many :coffee_shop_items
Coffee_shop_items has_and_belongs_to_many :coffee_orders
I have groups (Group model) in my app, which represent groups of people.
I want each group to have its own forum.
Should I just have the forum id in the groups table? It doesn't feel right. If I did it myself, the forum would have a polymorphic association to a "forumable" element (groups in this case, but I have other models that would need a forum).
Any opinions on what I should do? Modify the gem to fit my needs, or just have the forum_id in my models that need a forum? Or another solution maybe?
I'm the guy who started Forem (its the volunteers who did most of the hard work, though!), I think I can answer this question.
If you want only certain groups to have access to one and only one forum then you can put the forum_id field on the groups table and do it that way. What you can do then is override the can_read_forem_forum? method in your User model to perform a permission check for that user:
def can_read_forem_forum?(forum)
groups.where(:forum_id => forum.id).any?
end
This is used in Forem's ability model to determine whether or not a person can access a forum. What this method is going to do is that it will only return groups for that user that have link that specific forum. If there are any, then it's known that the user can access that forum.
Now if you're going the other route where a group may have access to many forums, well then you'd define a joins table between groups and forem_forums (called forum_groups) and define it as an association in your Group model like this:
has_many :forum_groups
has_many :forums, :through => :forum_groups, :class_name => "Forem::Forum"
You would need to also define a new model inside your application for this forum_groups association, it would be called ForumGroup and go a little like this:
class ForumGroup < ActiveRecord::Base
belongs_to :forum, :class_name => "Forem::Forum"
belongs_to :group
end
We're doing it this way so you have an easy way to manage the associations between forums and groups. If you did has_and_belongs_to_many, it generally only provides a gigantic pain in the ass when you want to delete one specific record from that join table.
Now, with that all nicely set up, the method you want to define in your User model is this one:
def can_read_forem_forum?(forum)
groups.joins(:forums).where("forem_forums.id = ?", forum.id).any?
end
Same thing, except this time we find all the groups that are linked to a specific forum through that association we set up earlier. This will do an INNER JOIN on the forum_groups table, and then another on the forem_forums table, getting the data required.
I hope this helps you, and thanks for using Forem!
I have two models, Clients and Items with a HABTM association. I would like to find the fastest way to select all Clients, which have several (more than one) of the items (or a certain number of items).
class Client < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :clients
end
The query below fetches the clients, which have any ONE item from the list:
required_item_ids = Item.where(:kind => :book).collect(&:id)
Client.join(:items).where(:items => {:id => required_item_ids})
But needed is the list of clients having SEVERAL or ALL of the required items.
I believe this is a simple question as I am a newbie to Rails, but I looked through all similar questions here and in some other places and did not find the answer.
If this is a one-off job, then you could get this list via looping through all the clients in Ruby. Something like:
clients_with_more_than_one_book_items = []
book_items = Item.where(:kind => :book)
Client.find_each do |client|
books_count = 0
client.items.each do |item|
if book_items.include?(item)
books_count += 1
end
end
clients_with_more_than_one_book_items << client if books_count > 1
end
However, that may take a while to run if you have lots of clients and items, so if this is a query that you're running often, it's probably a good idea to add a books_count attribute to the Client model, and to add code (within callbacks on the model) that keep this updated as a kind of counter cache.
I am working on a Ruby on Rails 3 web application and am not sure how to relate two of the models.
In our organization sales reps go out on appointments. If the appointment is successful, it will result in creating an order (which then has the items ordered related to it, but that's for another day.) If this appointment is not successful, it will be marked as no sale and as you might have guessed, no order is created.
On the other hand, sometimes sales happen without an appointment. For example, a customer may call into the store and order something. In this case, an order can exist without an appointment.
It would be simple if there were no relationship between orders and appointments, but there has to be for ease of use for the end user. For example, if an appointment generates an order, but later the buyer cancels, they will mark the appointment as sale cancelled and then the system should automatically set the order as cancelled. Likewise,they may choose to cancel the order, then the appointment would have to be cancelled automatically by the system.
How does a developer handle something like this? Does the appointment :have_many => orders? does the order :belong_to => appointments? I don't know what to do!
Please help me with this, I am a pretty new rails developer and I feel in over my head! Thank you!
As you already said, the following will work fine:
class Appointment < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :appointment
end
belongs_to requires the field appointment_id to be present in the orders table. But, if the order is not associated with an order then appointment_id does not need to be set. You can have multiple belongs_to associations for a given class.
I am starting to create my sites in Ruby on Rails these days instead of PHP.
I have picked up the language easily but still not 100% confident with associations :)
I have this situation:
User Model
has_and_belongs_to_many :roles
Roles Model
has_and_belongs_to_many :users
Journal Model
has_and_belongs_to_many :roles
So I have a roles_users table and a journals_roles table
I can access the user roles like so:
user = User.find(1)
User.roles
This gives me the roles assigned to the user, I can then access the journal model like so:
journals = user.roles.first.journals
This gets me the journals associated with the user based on the roles. I want to be able to access the journals like so user.journals
In my user model I have tried this:
def journals
self.roles.collect { |role| role.journals }.flatten
end
This gets me the journals in a flatten array but unfortunately I am unable to access anything associated with journals in this case, e.g in the journals model it has:
has_many :items
When I try to access user.journals.items it does not work as it is a flatten array which I am trying to access the has_many association.
Is it possible to get the user.journals another way other than the way I have shown above with the collect method?
Hope you guys understand what I mean, if not let me know and ill try to explain it better.
Cheers
Eef
If you want to have user.journals you should write query by hand. As far as I know Rails does has_many :through associations (habtm is a kind of has_many :through) one level deep. You can use has_many with finder_sql.
user.journals.items in your example doesn't work, becouse journals is an array and it doesn't have items method associated. So, you need to select one journal and then call items:
user.journals.first.items
I would also modify your journals method:
def journals
self.roles(:include => :journals).collect { |role| role.journals }.flatten.uniq
end
uniq removes duplicates and :inlcude => :journals should improve sql queries.
Similar question https://stackoverflow.com/questions/2802539/ruby-on-rails-join-table-associations
You can use Journal.scoped to create scope with conditions you need. As you have many-to-many association for journals-roles, you need to access joining table either with separate query or with inner select:
def journals
Journal.scoped(:conditions => ["journals.id in (Select journal_id from journals_roles where role_id in (?))", role_ids])
end
Then you can use user.journals.all(:include => :items) etc