Rails - check if record exists in has_many association
Asked Answered
R

4

31

I'm not sure if my question is worded correctly.

I have three models: User, Item, and UserItem.

user has_many :user_items
user has_many :items, through :user_items

item has_many :user_items
item has_many :users -> {uniq}, through :user_items
item belongs_to :user

user_item belongs_to :user
user_item belongs_to :item

I need a way to see if a user has an item to make if statements in my item views But here's the catch, user_items have enum status: [ :pending, approved]. So I need to see if a current_user has a certain :pending item.

For example when a user visits item1's view page I have the item_controller's show action declare @item = Item.find_by_id(params[:id]). But then what can I do with this @item to see if a user has this item?

Repatriate answered 8/4, 2016 at 2:36 Comment(0)
J
48

Try:

current_user.items.exists?(params[:id])

Or

current_user.items.exists?(@item.id)
Jacquelinejacquelyn answered 8/4, 2016 at 2:51 Comment(0)
K
7

Extending @lei-liu's answer here. One can find if the record exists among the many or not, through: current_user.items.exists?(params[:id])

At the same time, exists? allows one to filter through the columns besides id, and also allows for more complicated conditions, like the following:

current_user.items.exists?('id > 3')
current_user.items.exists?(name: 'some_name')
Karaganda answered 10/7, 2019 at 2:12 Comment(0)
T
0

But then what can I do with this @item to see if a user has this item?

I think what you are missing here is model methods. For example, if you added a method to the Item model called belongs_to_user_in_pending_state, you'd be able to call @item.belongs_to_user_in_pending_state(current_user) anywhere you need it.

def belongs_to_user_in_pending_state(user)
  if self.user_items.pending.select {|s| s.user == user}.count > 0 
    return true
  else
    return false
  end
end
Tall answered 8/4, 2016 at 5:11 Comment(5)
This wasn't written to be something to copy and paste, it was written as an example to show the OP how to write and use a model method... and how to use his pending enumerator. The select call is a little wasteful given the uniq limitation, but someone else has already shown how to check if something exists. Way to hate without offering anything of value @AndreyDeineko.Tall
1) you do not need self. 2) select would load the whole pending user_items collection into memory prior evaluating the block, thus, having tons records in collection it could potentially overwhelm the memory. 3) in this case you do not need to return. 4) you could just leave the user_items.pending.select {|s| s.user == user}.count > 0 since it will already return the boolean value. 5) sorry for the form of my first comment - it was the very morning - apologiesGrasshopper
@AndreyDeineko You mentioned a "conventional" way to accomplish this. What would that be?Repatriate
@Repatriate by the unconventional I meant the wrong use of return keyword here.Grasshopper
@AndreyDeineko - 1+2 - I thought this was a good way to cut down the memory needs - we should only be fetching the user_items for that item, and only those that are pending... I did not know the self was unnecessary!! 3) the return habit comes from C when i took comp sci many years ago - before getting out of programming for over a decade. 4) Was thinking to set him up for more complicated functionality - agreed for this case! 5) All Good... Thanks for returning to clarify! Always enthusiastic to learn the better way!Tall
C
-1

1) Add a scope to User_item class

scope :pending, -> {where status: 'pending'}

2) Use that scope in an instance method of Item class:

def is_pending_with_someone?
  self.user_items.pending.count > 0
end

Then you can use

if @item.is_pending_with_someone? 
  ...
Commissure answered 8/4, 2016 at 14:31 Comment(2)
I believe enum automatically adds scopeRepatriate
i don't think you want a count query. if you're just checking for existence using .exists? would give you a SELECT ... LIMIT 1 which would stop once a match was found.Chopper

© 2022 - 2024 — McMap. All rights reserved.