Accessing one element in a has many relationship - ruby-on-rails

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

Related

Rails - Creating and Validating Many-to-Many relationship with the same model?

This is probably a really simple question, but I've been searching the web for probably around an hour and I can't really find an answer to my problem. It should be clear by what follows that I am very new to Rails, so my terminology and explanation might be a bit confusing.
Let's say that I were making a social media app on Rails, where one of the models is User. I want to make a many-to-many relationship called "friends", which links two users together. Let's say in this situation I also wanted to make a many-to-many between two users called "enemies".
This is all completely hypothetical, but the idea is the same one that I want to use for something I'm working on.
Because a user can have many friends and enemies, but also be many friends and enemies, I would use:
class User < ActiveRecord::Base
has_and_belongs_to_many :users #this should be the friends association
has_and_belongs_to_many :users #this should be the enemies association
end
Now I'm guessing I can't just do that, because I would have to have two tables both named users_users. So, then I switch to:
class User < ActiveRecord::Base
has_and_belongs_to_many(:users, join_table: 'friends',
foreign_key: 'user_id', associate_foreign_key: 'friend_id')
end
With a similar statement for the enemies table. Now, my problem is that I want to have a form that the user can use when they sign up, where they can input their information (this is the User object details), and also list their friends and enemies.
Because the user won't have the database id key for their friends or enemies, they'll have to input the users' names. This is fine, though because the name is also a unique key, guaranteed by the validation.
However, if the user types in the name of a friend, I can't join the two if the friend happens to not exist. So, I use a custom validation class that looks something like this:
class FriendValidator < ActiveModel::Validator
def validate(object)
#lookup user and throw error if not found.
end
end
which will access the variable (object.friends) and (object.enemies)
With something similar for enemies. So therefore, above my has_and_belongs_to_many statements, I have lines that say:
attr_accessor :friends, :enemies #these are attrs because they don't exist within the model's db
validates_with FriendValidator
When I create the form with erb, I have the standard form_for block
<%= form_for(#user) do |f| %>
It seems to me that I can't just stick
<%= f.text_area :friends %>
because friends isn't actually something that will get passed to the User object, but rather a separate table. (Can I, though? Because the attr_accessor is declared in the user's model class?)
So now, we have my main problem. I have two many-to-many tables with a model to its own model class, and I don't know how to ensure that the validation class will take the two attributes, lookup and throw necessary errors, and then add a row to the join tables using the id of the user, rather than the string inputted. What form fields should I use to pass the input to the right place? Where do I change the controller methods so that the input gets sent to the join table rather than the user object?
This definitely seems like a pretty specific situation, so I can't really find an answer in the Rails documentation, which I've been learning from.
My initial impression of this problem has to do with your associations. To me, a user has_many enemies and has_many friends.
friends belong_to user
enemies belong_to user
Not sure if a many to many relationship makes sense in this case. Maybe that's why you are having such a hard time finding an answer online. Just my two cents.

How find all the associations of an instance

This question RAILS: How to get has_many associations of a model tells how to find all the associations of a Class. I want to do this for an instance of the class. In particular I have a User model, and when I setup a User instance, it has a number of associations e.g. user.profile, user.plans etc. I want to check all the associations have been successfully set up for a particular user instance. How do you do this?
Based on the link you provided you should be able to accomplish what you want by doing this:
User.reflect_on_all_associations.map { |assoc| assoc.name }.each do |assoc|
association_object = user.send assoc
#note this is the user instance not the class.
# do whatever you want with association_object. check if nil?
end
What the code does, it to iterate through the list of association name keys returned the link you provided and then use it to call the "method" (meaning the association) by using send.
Hope that helps

Having trouble in tracing code in Rails

I am currently struggling with this piece of code
#play = current_user.playlist.find_by_id(params[:id])
What does current_user.playlist.find_by_id() mean? How can I trace this code to find current_user, playlist and find_by_id() function?
You could be using devise, if that is the case, current_user returns an instance of class User which is the currently logged in user. Or nil if there is no logged in user.
playlist is a method defined in class User, you should find this class in app/models/user.rb, usually this method would be defined with:
has_one :playlist
or:
belongs_to :playlist
find_by_id is a method defined by Rails for class User, you won't see this directly in file. It is created when you have something like this:
class User < ActiveRecord::Base
ActiveRecord::Base creates a lot of methods more.
Debug your code
I would print each part of the sentence
p current_user
p current_user.playlist
p params[:id]
p current_user.playlist.find_by_id(params[:id])
and check results in my server console, you could spot which of these is the first nil.
In Ruby, everything is an object, and objects have methods. Objects also have types.
Your top-level object there is current_user. This is an instance of some class - you can find out what kind by looking at current_user.class. I suspect you're going to find that it's an instance of User.
So, you find where your User is defined. This is likely a model, defined in app/models/user.rb. This model will specify a number of attributes and associations. In this case, you likely have a has_many :playlists association. What this does is set up an association between a User instance and a number of Playlist instances. Given a user instance, user_instance.playlists accesses this association. Your Playlist model will have a user_id field that associates a playlist with a user record. You can read more about associations in the relevant documentation.
Finally, this association will have a number of methods from Rails. ActiveRecord has a standard set of finders, as well as some "magic" finders like find_by_id, which infer the field to find from based on the method name. find_by_id(params[:id]) is functionally equivalent to something like find_by(:id => params[:id]), but it's a little more English-y. You can read more about this in the Dynamic Finders method of the documentation.
find_by_id will generate the SQL necessary to find the playlist records with that ID that also have a user_id matching current_user's ID. If it finds a matching record, it will instantiate a Playlist record with the data it retrieved and return it. If no matching record is found, it will return nil.

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