Failing to save attributes of associated object in rails application - ruby-on-rails

I have three models defined as follows
class Student < ActiveRecord::Base
belongs_to :user
has_many :placements
has_many :companys , through: :placements
end
class Company < ActiveRecord::Base
has_many :placements
has_many :students , through: :placements
end
class Placement < ActiveRecord::Base
belongs_to :student
belongs_to :company
before_save :set_placed
def set_placed
s = self.student
s.is_placed = true
s.save
end
end
Each time i add data for placement object i want to update a field in its corresponding student object. But when i use rails_admin to add data , i am getting the error Placement failed to be created .
When i remove the before_save call , data can be added.
I am using better_errors gem for debugging. I am getting the following from it
#_already_called
{[:autosave_associated_records_for_student, :student]=>false,
[:autosave_associated_records_for_company, :company]=>false}
i am hoping this could be the reason for error.
How can i solve this error??

You have a s.save in your set_placed callback. You don't save an ActiveRecord object in a callback, and especially not in a before_save callback.

try this,
def set_placed
self.student.is_placed = true
end

Related

Rails getting data from nested attributes

I have three models:
data_set.rb
class DataSet < ActiveRecord::Base
has_many :browse_options
accepts_nested_attributes_for :browse_options, allow_destroy: true
end
browse_option.rb
class BrowseOption < ActiveRecord::Base
belongs_to :data_set
has_many :browse_option_datas
accepts_nested_attributes_for :browse_option_datas, allow_destroy: true
end
browse_option_data.rb
class BrowseOptionData < ActiveRecord::Base
belongs_to :browse_options
has_one :tradesman
end
I want to be able to display all the tradesman associated with a data set in the data set view with no duplicates. Is there a way I can use the joins method to do this in the controller? Thanks!
You can actually achieve this by setting up has_many through relationships between your models. There are great docs on this topic.
class DataSet
has_many :browse_options
has_many :browse_option_datas, :through => :browse_options
has_many :tradesmen, :through => :browse_option_datas
end
class BrowseOption
belongs_to :data_set
has_many :browse_option_datas
end
class BrowseOptionData
belongs_to :browse_options
belongs_to :tradesman
end
class Tradesman
has_many :browse_options_data
end
Edit: After some discussion in chat we also realised the relationships between Tradesman & BrowseOptionData needed some fixing.
Now, in your controller, you can call:
#data_set = DataSet.first
#tradesmen = #data_set.tradesmen # .uniq if you don't want any duplicates

Rails: ActiveRecord has_many association not working

I'm a bit new to Rails Active Record associations. I've tried to set up a relationship, but I get ActiveRecord error when I try to retrieve data. Did I associate my models incorrectly?
User has many Uploads, which has many UserGraphs:
class User < ActiveRecord::Base
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end
I want to get all of a user's uploads, and all of a user's graphs. The 2nd line doesn't work in rails console and gives an error
#user = User.find(1)
#uploads = #user.uploads
The error:
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_graphs in model User
Extra Credit:
If Users have Uploads that have UserGraphs... shouldn't it be has_many :uploads and has_many :user_graphs, through :uploads?
Add
has_many :user_graphs
to the User and Upload classes.
The :through option defines a second association on top of this one.
You didn't tell Rails that you have a user_graphs association on User, only an uploads association. So when Rails goes to follow the user_graphs association on uploads, it can't find it.
So, you need add the user_graphs association. Your models should look like this:
class User < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :uploads, through: :user_graphs
end
class Upload < ActiveRecord::Base
has_many :user_graphs # <<< Add this!
has_many :users, through: :user_graphs
end
class UserGraph < ActiveRecord::Base
belongs_to :user
belongs_to :upload
end

Association dilemma

What is the best way to implement something like a board in pinterest (a collection of objects) in rails. I am trying to come with it, it seems more like an array implementation.
Here's my logic for the associations : User have many collections, user have many pins , collections belongs to user.
User class
class User < ActiveRecord::Base
has_many :pins, through: :collections
has_many :collections
end
Pins class
class Pin < ActiveRecord::Base
belongs_to :user
has_many :collections
end
Collections class
class Collection < ActiveRecord::base
belongs_to :user
end
So now here's my confusion, how to implement a controller that will allow me to create a collection and inside this collection object, create or push pins and save them as another object for the current_user. Hope I'm making sense
Here's the controller
class CollectionsController < ApplicationController
def create
#collection = current_user.collections.new(params[:collection])
#this where i'm confused , if it an array , how to implement it , to push or create a pin object inside ?
end
end
You have to use nested attributes for this.
Check this http://currentricity.wordpress.com/2011/09/04/the-definitive-guide-to-accepts_nested_attributes_for-a-model-in-rails-3/.
Basically what you need is:
# collection model
accepts_nested_attributes_for :pins
# view, see also nested_form in github
f.fields_for :pins
You're looking for the has_many_through association. See section 2.4 on the Rails guide: http://guides.rubyonrails.org/association_basics.html
class User < ActiveRecord::Base
has_many :collections
end
class Pin < ActiveRecord::Base
has_many :collections, through: :pinnings
end
class Pinning < ActiveRecord::Base
belongs_to :pin
belongs_to :collection
end
class Collection < ActiveRecord::base
belongs_to :user
has_many :pins, through: :pinnings
end

rails - how to pass changes back to parent model

I need to pass updated paramaters to back to a parent model when saving a series of its children.
For example if a save a bunch of employees to each task through a project, I need to let the project know the title of some of its tasks have changed, then I need to collect all the titles that changed and process them in the ProjectObserver. Is this possible?
I realize there might not be a way to make this work the way I'm trying. If not I'm happy to hear suggestions about how I might be able to get around this.
Here is what I have tried without any success:
class Employee < ActiveRecord::Base
has_many :employee_tasks
has_many :tasks, :through => :employee_tasks
accepts_nested_attributes_For :employee_tasks
accepts_nested_attributes_For :tasks
end
class Project < ActiveRecord::Base
attr_accessor :changed_employees
has_many :tasks
end
class Task < ActiveRecord::Base
has_many :employee_tasks
has_many :employees, :through => :employee_tasks
belongs_to :project
accepts_nested_attributes_For :employee_tasks
end
class EmployeeTask < ActiveRecord::Base
#this is what I want to accomplish
before_save do
if self.employee_id_changed
self.task.project.changed_employees ||= []
self.task.project.changed_employees << self.employee_id_changed
end
end
belongs_to :task
belongs_to :employee
end
class ProjectObserver < ActiveRecord::Observer
observe :project
def after_save(project)
puts project.changed_employees
# should print out the changed attributes loaded from EmployeeTask
#send a single email with all the updated titles (not one email for each change)
end
end
Sounds like you need to use the after_save method described here http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

How to delete nested objects in Rails3?

How can I delete nested objects in a form? I found out that I need to add :allow_destroy in the parent model at the accepts_nested_attributes_for directive.
Further, I want to restrict the deletion. A nested object only should be deleted, if the parent object is the only one that retains the association.
Example:
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships
end
Explanation: A company can host many internships. Therefore, I do not want to delete the company record as long as there is at least one other internship associated with it.
You could use dependent => :destroy
class Internship < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company, allow_destroy => true
end
class Company < ActiveRecord::Base
has_many :internships, :dependent => :destroy
end
If you return false in a before_destroy filter, then the destroy action will be blocked. So we can check to see if there are any internships associated to the company, and block it if so. This is done in the company model.
class Company < ActiveRecord::Base
has_many :internships
before_destroy :ensure_no_internships
private
def ensure_no_internships
return false if self.internships.count > 0
end
end

Resources