failing to create Active Record Association - ruby-on-rails

This is an extremely simple Active Record association I am trying to create and it is frustrating that is is not being made successfully.
I have two models, post and user.
User.rb has nothing but has_many :posts
and Post.rb has nothing but belongs_to :user.
I have run rake db:migrate and verified that there is a user_id column in my posts table.
When I go to the console, though, I am unable to make an association between new objects.
First, I make a new User instance like max = User.create(:name=>"Max")
Next, I make a new Post instance like post = Post.create(:user_id=>1, title=>"FirstPost")
I then try and type max.posts but get a NoMethodError undefined method 'post='
If I try and set up the association like max.post = post, I get the same error.
Lastly, I tried adding attr_accessor :posts to the User model.
Now, I can type max.posts, but I am just getting nil.
What am I missing here?

That's because there's no 'post=' method in User.
Try the following:
max = User.create(:name=> "Max")
max.posts.create(:title => "FirstPost")
max.posts
As an alternative way:
max = User.create(:name=> "Max")
post = Post.new(:user => max, :title => "FirstPost")
post.save
max.posts

Related

Rails Associations don't work

I've read through many tutorials, and copied their code exactly, yet what they claim works for them doesn't work for me.
I'm making a most basic "has_many" and "belongs_to" association, but rails refuses to acknowledge any association whatsoever.
A user "has_many" emails. Emails "belong_to" user. Here's my code:
user.rb
class User < ActiveRecord::Base
unloadable
has_many :emails
accepts_nested_attributes_for :emails,
:allow_destroy => true,
# :reject_if => :all_blank
end
email.rb
class Email < ActiveRecord::Base
unloadable
belongs_to :user
end
Then, in the console:
User.emails.build
NoMethodError: undefined method `emails' for #<Class:0x00000006c16e88>
Indeed, this "NoMethodError" persists no matter what.
As of now, my guess is that a capacitor in my hardware burnt out while I was installing rails, causing everything to work except this one thing. Or maybe it's something else :p
EDIT:
Another console attempt:
my_user = User.new
my_user.emails.build
Also results in an undefined "emails" method.
I noticed that my original user class has a bad comma at the end; removing that, I get this error:
ActiveRecord::UnknownAttributeError: unknown attribute 'user_id' for Email.
First, you'll need to make sure you have a user_id attribute on your email table in the database. If you don't have one, you can add it with a migration.
Then, you need to tell Rails which instance of a user's emails you want to look at. So, you'll need to make sure you have a user in the database (user = User.create) and then that user's emails can be found using user.emails.
You're confusing the concept of classes and instances. You need an instance of the User class in order to build associated relations. The error you're getting (NoMethodError: undefined method emails for #<Class:0x00000006c16e88>) hints to this, since it's telling you you're trying to call the method emails on a Class object.
Try something like:
my_user = User.new
my_user.emails.build
Please use like this
#email = User.first.emails.build

Rails .includes() returning 'undefined method'

I have a Company object with an associated Location.
Company has_many :locations
Location belongs_to :company
When I call:
#company = Company.find(5).includes(:locations)
I get the error:
NoMethodError: undefined method `includes' for #<Company:0x000000066ad398>
I'm following the rubyonrails.org instructions to a T... as far as I can tell.
find() triggers a database query, and returns an instance of the model. You can't call includes on it, as includes is supposed to have an ActiveRecord::Relation as receiver.
You need to invert the order: find must stay at the end:
#company = Company.includes(:locations).find(5)
Did you check the records in the database on location.rb model. The company_id is there?
You can use pry-rails in order to debug the code. Your code looks ok.
The json you are creating is with jbuilder? because is that the case, you need to create the corresponding each loop.

How to build objects that avoid a ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection erros in rails?

I am experiencing the ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection error when trying to build a new object in my rails app. It does not fit any of the standard errors I've seen, and can't be fixed with inverse_of associations.
I presume I need to run a callback to help this work - can anyone help fix the issue below:
def PhoneNumber do
belongs_to :key_contact
end
def KeyContact do
has_many :phone_numbers
has_many :sale_contacts
end
def SaleContact do
belongs_to :key_contact
belongs_to :sales_opportunity
has_many :phone_numbers, through: :key_contact
accepts_nested_attributes_for :phone_numbers
end
As you can see, SaleContact is the join table where key_contacts and sales_opportunities meet - basically I'm picking existing key_contacts and displaying them on a sales_opportunity page with some additional details (role, preference etc - I've excluded this for brevity).
When adding a new sale_contact I want to offer users the ability to also add phone_numbers at the same time. This is throwing my activerecord error.
My SaleContact Controller:
def new
#sale_contact = SaleContact.new
#phone_number = #sale_contact.phone_numbers.build
end
This works to show the fields_for phone_number on the input form, and passed the right attributes through the params hash for adding a new phone_number, but that's when I get the error:
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection (Cannot modify association 'SaleContact#phone_numbers' because the source reflection class 'PhoneNumber' is associated to 'KeyContact' via :has_many.):
From what I can see:
My new controller action builds a phone_number, but because the sale_contact does not yet know which key_contact it's associated with I presume ActiveRecord gets confused
If I try and remove the #sale_contact.phone_number.build line (replacing it with PhoneNumber.new for example) the fields no longer appear on the SaleContact new form
As such I was thinking of creating a callback to strip out the phone_number_attributes from the sale_contact hash, destroy the newly built phone_number and all associations, then start fresh by passing the phone_numbers_attributes to a PhoneNumber.new(phone_number_attributes) action and saving as a separate transaction. Would that work?
You could try this instead:
delegate :phone_numbers, to: :key_contact

Updating an ActiveAdmin nested resource using inputs not located on the table

I currently have a form for a nested resource in one of my activeadmin pages:
f.inputs "Courses" do
f.has_many :registrations, :allow_destroy => true, new_record: true do |tc|
tc.input :course
tc.input :semester
end
end
This is for a student resource. A student has many course_offerings through registrations. However, I didn't want users to select the course_offering directly from a menu (Since there will be many repeats of the same course each year). Instead there is an input for course and semester. course and semester are instance variables on the registration model. They are set in the form, then the correct course_offering is found in an after save hook and associated with the registration. The code is as follows:
def semester=(s)
#semester = s.to_i
end
def semester
self.course_offering.semester
end
def course=(co)
#course = co.to_i
end
def course
self.course_offering.course
end
before_save :set_course_offering
def set_course_offering
self.course_offering = CourseOffering.where(semester_id: #semester, course_id: #course).first
#TODO: Handle case where no course offering is found
end
I am having two problems. The first is that I get a nil pointer error when registrations table is empty.
undefined method `course' for nil:NilClass
I have accepts_nested_attributes call in my student model.
accepts_nested_attributes_for :registrations, :allow_destroy => true
which is the only suggestion I get when looking up the error but still get it despite having that piece of code. It seems to work fine when I remove the course and semester and replace it with a course_offering instead.
The next problem I have is that the student record does not save after hitting update. I assume this is because I don't make any changes that write to the database when I only update the two course and semester instance variables only. I either need to update another input or add the call to the semester= method.
You get an undefined method error because when the registration table is empty there are no course offerings associated with your student, so in the accessor method for course you get nil for self.course_offerings. You could try this instead which takes the nil value into account:
def course
self.course_offering.try(:course)
end
You don't need the accept_nested_attributes_for because you don't want to create or modify course offerings through students.
For your second problem: you're right about the dirty tracking. Your model is not saved because your student model has not been modified from the perspective of ActiveRecord. You need to flag an attribute (eg course_offering) as dirty by hand with the course_attribute_will_change! method before saving the model.
Although this situation looks like a good example to introduce form objects. There is a great library for that called reform.

Associations in Rails

i am new to Rails ..
i am having a Table named users (id,name)
and another table which has the additional information of the user called
user_details(id,user_id,additional_info) where additional_info is a hash .
In the User Model i added a line
has_one :user_details
And in the User_Detail model i added a line
belongs_to :user
serialize :additional_details, Hash
Now in the Users Controller i am having an action
# set_user_empid to set the hash value empid in the additional_info column for the current_user
def set_user_empid
#user1 = current_user
#user_detail1=#user1.user_details
#user_detail1.additional_details[:empid] = params[:value]
#user_detail1.save
render :text => CGI::escapeHTML(#user_detail1.additional_details[:empid].to_s)
end
The above one #user1.user_details shows me the error as
NameError (uninitialized constant User::UserDetails):
But the same thing if i change the has_one to has_many i am getting the actual result...
Please give suggestions...
The quick fix here is to change has_one :user_details to has_one :user_detail, but really what you want is to get rid of the UserDetail model entirely and just move the column into the User model, so the users table has these columns: id, name, additional_info and then move the call to serialize into the User model. No real reason to have a separate table just for metadata.
I believe since you are using user_details, pluralized, it is not able to pick it up. Could you try using has_one :user_detail

Resources