Rails find using where in one to many relation - ruby-on-rails

I have a User model that has many "states":
class User
has_many states
class State
belongs_to user
Now, I'd like to go through each User and find all the Users with that state something like...
state = State.last
# I want to get all the users where state appears in ANY user's user.states
User.where(state IN user.states.. )
Any advice would be appreciated!

One way is to use a join to get the user's that have the state:
User.joins(:states).where(state: 'some_type_of_state')
The other way is by grabbing all the user id's that have the specific state, and then querying for those users:
user_ids = State.where(state: 'some_type_of_state').pluck(:user_id)
users = User.where(id: user_ids)
The last query typically doesn't perform as well.

Related

Accessing one element in a has many relationship

I have two classes with one-to-many relationship. For example:
class User
has_many :numbers
...
end
How can I access one individual number that user has without using for or each? I tried user.numbers[2] and it didn't work (I was thinking it's like using a basic array but apparently it's not).
First find the user
user = User.find(1) or user = User.first
user.numbers.first #this will return the first associated object

Figuring out relationships for a Document model that can be editted by multiple users

I'm trying to figure out how to set up the relationship/association for a Document model for my rails project. I have a User model and Users can friend each another (friendship model).
Now I want users to be able to invite (give access to) CERTAIN friends to modify and edit these documents similar to Google Docs.
This is my current approach to this problem:
Create a new relationship model called "group", essentially a subset of friends. Document belongs to a user and document belongs to a group. Users can then invite their friends into these group relationships and documents can be accessed/modified through these groups.
Therefore, User has many groups and group belongs to many users.
My question:
Is there a better approach to this problem?
You might consider a "Membership" join table. So:
Group >- Membership -< User
...since you requirement is some users will have abilities others wont (editing). There are all sorts of options from there, for example STI which would give you:
class Membership < ActiveRecord::Base
...general membership functions
end
class Editor < Membership
...editor specific code...
end
class Reviewer < Membership
...reviewer specific code...
end
And a controller class something like
class MembershipController
def create invitation
if invitation.for_editor?
# assuming invitation has one group, and group_memberships method that returns group.memberships
invitation.group_memberships.create! user: current_user
else
invitation.group_memberships.create! user: current_user
end
end
end
As an example, depending on your opinion of STI.
https://github.com/scambra/devise_invitable
might have more ideas.

Rails / Devise: Create user and parent org at once

Using Devise.
Models:
User belongs_to Organization
Organization has_many Users
During signup, I want to create the user's parent organization as well. So two pieces to the form: 1) organization info, and 2) basic user info (email/password)
I've done a bunch of searching for Devise and nested resources, but they usually talk about the model relationship being the other direction (User has_many).
Any ideas?
Thanks in advance!
Do you already know the organisations that the user could belong to?
In which case just have a drop down when they register and insert the ID of the organisation on save.
Otherwise, what will probably happen is that you basically end up with a 1:1 with organisation anyway given typos and so on, unless you are guessing based on the name they input. Does the organisation have any kind of security associated with it? If this is a public site it seems a little dangerous because people could camp in places they're not supposed to be.
That said:
o = Organisation.find_or_create_by_name(params[:org_name])
u = o.user.build(params[:user])
if u.save ... # etc.
or something like that.
u = User.new(params[:user])
if u.valid?
o = Organisation.create(params[:org_name])
u.organization = o
u.save
end

Ruby on rails activerecord joins - select fields from multiple tables

models:
#StatusMessage model
class StatusMessage < ActiveRecord::Base
belongs_to :users
default_scope :order => "created_at DESC"
end
#User Model
class User < ActiveRecord::Base
has_many :status_messages
end
In controller I want to join these two tables and get fields from both table. for example I want email field from User and status field from StatusMessage. When I use :
#status = User.joins(:status_messages)
Or
#status = User.includes(:status_messages)
It gives me only the user table data.
How can I implement this requirement?
You need to use includes here. It preloads data so you won't have another SQL query when you do #user.status_messages.
And yes you can't really see the effect: you need to check your logs.
First of all, I don't think it is possible (and reasonable) what you want to do. The reason for that is that the relation between User and StatusMessage is 1:n, that means that each user could have 0 to n status messages. How should these multitudes of attributes be included in your user model?
I think that the method joints in class ActiceRecord has a different meaning, it is part of the query interface. See the question LEFT OUTER joins in Rails 3
There are similar questions on the net, here is what I have found that matches most:
Ruby on Rails: How to join two tables: Includes (translated for your example) in the user a primary_status_message, which is then materialized in the query for the user. But it is held in one attribute, and to access the attributes of the status_message, you have to do something like that: #user.primary_status_message.status
When you use #status = User.includes(:status_messages) then rails eagerley loads the data of all the tables.
My point is when you use this User.includes(:status_messages) it will loads the data of status_messages also but shows only users table data then if you want first user status_messages then you have to #user.first.status_messages

Why doesn't this associated create call work?

I've got a User model that has many Items. A Rating belongs to a User and an Item.
In the DB, I have set ratings.user_id to be not NULL.
when I am creating an Item, I would like to do this:
def create
current_user.items.create(params[:item]).ratings.create(params[:rating]
redirect_to items_path
end
However, this balks with an SQL error "user_id cannot be nil"
so I rewrote the create method as
def create
current_user.items.create(params[:item]).ratings.create(params[:rating].merge({:user_id => current_user}))
redirect_to items_path
end
which works fine.
However, I had thought that chaining the create methods off the current user's receiver would have populated the rating's user_id. Anyone know why not?
TIA.
I'd recommend you normalize this if possible in the database. Maybe take out the user_id attribute from the ratings table and if you need it in your model get it through a join using a :through method
class Rating
has_many :items
has_one :user, :through=>:items
If you created and saved the Item, then made a Rating from that item, it wouldn't pass the user along to the Rating, right? You'd just refer to it as #rating.item.user, right?
When you think about it like that, you wouldn't expect the Item created via the current_user to pass the user information along to the rating.
Make me wonder if you really need the user has_many ratings relationship.
Because Item has many Ratings and that association does not know about the user id. Given that association chain Item would have a user id because it belongs to a user. And Rating would have an item id because it belongs to an item. But the Item to Rating assocation doesn't know anything about a user unless you tell it.

Resources