Rails Associations don't work - ruby-on-rails

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

Related

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

Rails: before_creating one model, go create a second model first

I have 2 models - User and Alias, where User has_many :aliases.
When creating a User, it must be unique to any Alias that already exists.
When a User is created, they also get an Alias saved with that User's name.
Here is the code for my User.rb model
validates_associated :aliases # bring in any validations from relationship models
before_create :create_alias
def create_alias
a = self.aliases.new
a.alias = username
return a.save
end
The alias model validation is validates_uniqueness_of :alias.
My theory is, that before I create a User model go create an Alias model and if that fails, then creating the User model should also fail.
However, when it fails, rails is exploding.
It's not doing the validates_associated properly.
How can I accomplish what I want to do?
check this http://guides.rubyonrails.org/association_basics.html#has-many-association-reference, you can use the :autosave and :validate options when you define the association so you can just remove that validates_associated and your before_create callback and let rails handle those actions
has_many :aliases
EDIT: I think the problem is that you are assigning new alias AFTER the validations! so, you have to build the new alias right when you assign the username of after validations
def username=(value) #custom setter for username
aliases.build(alias: value)
write_attribute(:username, value)
end
now the alias will be there before the validations and user.valid? will run the alias' valdations too when validating
This must be impossible to do in a model. At least with devise...
To fix this problem I just gave up trying and moved the config to the controller with something like this:
#user.aliases.build(:name => #user.username)

failing to create Active Record Association

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

How to implement a last_modified_by (person) attribute on two unrelated models - Rails

I have a Record model and in order to edit this model, you must be logged in as an instance of Admin. I would like to have a column called last_modified_by which points to the Admin who last modified the Record. In the database, I was thinking it would be good in the records table to add a column that holds the Admin's id; however, the only way I know how to do that is with an association. These two models are not associated with each other so an association doesn't make a lot of sense. Is there any other way I might be able to accomplish this task without resorting to associations? Any advice would be much appreciated!
Hmm, I think the association is a good tool here. You might want to try to hack it somehow but I think nothing you can conjure up will ever be as good as an association via a foreign_key(also so fast). But perhaps you would like to name your association and do something like:
class Record < ActiveRecord::Base
belongs_to :culprit, :class_name => 'Admin', :foreign_key => 'last_modified_by'
end
or give it some more senseful naming?
You could create an Active Record before_save callback. The callback would save the admin's id into the last_modified_column. This would make sure the admin id is saved/updated each time there is a change to the model.
For example, assuming admin is #admin:
class Record < ActiveRecord::Base
before_save :save_last_modified
def save_last_modified
self.last_modified_column = #admin.id
end
As for getting #admin, you could employ a method similar to this, and set #admin = Admin.current (like User.current in the link) somewhere in the Record model.

How can I access ActiveRecord Associations in class callbacks in rails?

Updated
Appears to be a precedence error and nothing to do with the question I originally asked. See discussion below.
Original question
Is it possible to use active record associations in callbacks? I've tested this code in the console and it works fine as long as it isn't in a callback. I'm trying to create callbacks that pull attributes from other associated models and I keep getting errors of nil.attribute.
If callbacks are not the correct approach to take, how would one do a similar action in rails? If the associations are simple, you could use create_association(attributes => ), but as associations get more complex this starts to get messy.
For example...
class User < ActiveRecord::Base
belongs_to :b
before_validation_on_create {|user| user.create_b} #note, other logic prevents creating multiple b
end
class B < ActiveRecord::Base
has_many :users, :dependent => destroy
after_create{ |b| b.create_c }
has_one :c
end
class C < ActiveRecord::Base
belongs_to :b
after_create :create_alert_email
private
def create_alert_email
self.alert_email = User.find_by_b_id(self.b_id).email #error, looks for nil.email
end
end
Off course associations are available in your callbacks. After all, the create_after_email is simply a method. You can even call it alone, without using a callback. ActiveRecord doesn't apply any special flag to callback methods to prevent them from working as any other method.
Also notice you are running a User#find query directly without taking advantage of any association method. An other reason why ActiveRecord association feature should not be the guilty in this case.
The reason why you are getting the error should probably searched somewhere else.
Be sure self.b_id is set and references a valid record. Perhaps it is nil or actually there's no User record with that value. In fact, you don't test whether the query returns a record or nil: you are assuming a record with that value always exists. Are you sure this assumption is always statisfied?

Resources