I keep getting this undefined error when I try to submit this form with nested attributes, not sure where it's coming from been wrestling with it for quite a while now, I am trying to let users select an option in the council model the submit their choice, I am not sure if I have my associations wired up correctly or if the error is coming from the form. Am a noob to rails. Thanks in advance.
Error Updated
Properties::BuildController#update
app/controllers/properties/build_controller.rb, line 21
Started PUT "/properties/5/build/council" for 127.0.0.1 at 2013-08-18 08:52:07 +0100
Processing by Properties::BuildController#update as HTML
Parameters: {"utf8"=>"✓","authenticity_token"=>"wBWQaxtBioqzGLkhUrstqS+cFD/xvEutXnJ0jWNtSa0=", "council_id"=>"1", "commit"=>"Save changes", "property_id"=>"5", "id"=>"council"}
Property Load (0.2ms) SELECT "properties".* FROM "properties" WHERE "properties"."id" = ? LIMIT 1 [["id", "5"]]
Completed 500 Internal Server Error in 35ms
NoMethodError - undefined method `[]=' for nil:NilClass:
Council View
<h1>Select Council</h1>
<%= form_tag url_for(:action => 'update', :controller => 'properties/build'), :method => 'put' do %>
<%= select_tag :council_id, options_from_collection_for_select(Council.all, :id, :name) %>
<%= submit_tag %>
<% end %>
Controller
class Properties::BuildController < ApplicationController
include Wicked::Wizard
steps :tenant, :meter, :council, :confirmed
def show
#property = Property.find(params[:property_id])
#tenants = #property.tenants.new(params[:tenant_id])
#meter = #property.build_meter
#council = #property.build_council
render_wizard
end
def edit
#property = Property.find(params[:property_id])
end
def update
#property = Property.find(params[:property_id])
params[:property][:status] = step.to_s
params[:property][:status] = 'active' if step == steps.last
#property.update_attributes(params[:property])
render_wizard #property
end
end
Council.rb
class Council < ActiveRecord::Base
attr_accessible :CouncilEmail, :name, :CouncilTel
belongs_to :property
end
UPDATED Propery.rb
class Property < ActiveRecord::Base
attr_accessible :name, :address_attributes, :tenants_attributes, :meter_attributes, :council_attributes, :property_id, :status
belongs_to :user
has_one :address, :as => :addressable
accepts_nested_attributes_for :address, :allow_destroy => true
has_one :council
accepts_nested_attributes_for :council, :allow_destroy => true
has_many :tenants, :inverse_of => :property
accepts_nested_attributes_for :tenants, :allow_destroy => true, :reject_if => :all_blank
has_one :meter
accepts_nested_attributes_for :meter, :allow_destroy => true
validates :name, :presence => :true
validates :address, :presence => :true
validates :tenants, :presence => true, :if => :active_or_tenants?
validates :council, :presence => true, :if => :active_or_council?
def active?
status == 'active'
end
def active_or_tenants?
(status || '').include?('tenants') || active?
end
def active_or_council?
(status || '').include?('council') || active?
end
end
I think this
params[:property]
is nil. So Ruby complains when doing
params[:property][:status] = 'foo'
You might want to do something like this:
if params[:property]
params[:property][:status] = 'foo'
end
However in your case the issue is because you are using a form_tag instead of a form_for, therefor params[:property] is not defined.
A better approach to check for nested attributes in ruby hashes nowadays is to use dig
Example:
params.dig(:property, :status)
If the key is not defined nil is returned.
If using a data file, make sure there's not a stray - in the data.yml. See this GitHub comment
Related
i have added the following setter/getter methods to my model, though whenever i try and save the form i am getting an error about mass assignment. from my understanding how this should work is, that if the opponent_name cant be found it will add a entry to the database
def opponent_name
opponent.try(:name)
end
def opponent_name(name)
self.opponent = Opponent.find_or_create_by_name(name) if name.present?
end
here is the error from the console log
Started POST "/events" for 127.0.0.1 at 2013-03-26 19:07:26 +1100
Processing by EventsController#create as JS
Parameters: {"utf8"=>"✓", "authenticity_token"=>"h7OrLKeDL/9KmZeGZeO+QTWHtlUdOlaMqnoMGhYaDUU=", "event"=>{"datetime(3i)"=>"2", "datetime(2i)"=>"3", "datetime(1i)"=>"2013", "datetime(4i)"=>"00", "datetime(5i)"=>"00", "event"=>"1", "location_id"=>"7", "duration"=>"30", "arrival_time"=>"30", "opponent_name"=>"Test", "home_or_away"=>"Home"}, "commit"=>"Create Event"}
User Load (0.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = 4 LIMIT 1
Completed 500 Internal Server Error in 14ms
ActiveModel::MassAssignmentSecurity::Error (Can't mass-assign protected attributes: opponent_name):
app/controllers/application_controller.rb:22:in `catch_not_found'
opponent model
class Opponent < ActiveRecord::Base
has_many :events
belongs_to :team
attr_accessible :name, :team_id
validates :name, :presence => true
end
event model
class Event < ActiveRecord::Base
include PublicActivity::Model
tracked
belongs_to :location
belongs_to :opponent
belongs_to :team
belongs_to :result
has_many :availabilities, :dependent => :destroy
def opponent_name
opponent.try(:name)
end
def opponent_name(name)
self.opponent = Opponent.find_or_create_by_name(name) if name.present?
end
attr_accessible :location_id, :user_id, :datetime, :score_for, :score_against, :event,
:team_id, :home_or_away, :arrival_time, :duration, :selected_players, :date, :time, :result_id
validates :event, :location_id, :team_id, :presence => true
validates_numericality_of :team_id, :only_integer => true, :greater_than_or_equal_to =>0, :message => " needs to be set, please contact your Administrator"
#validates_numericality_of :arrival_time, :only_integer =>true, :greater_than_or_equal_to =>0, :message => " must be greater than 0 minutes", :allow_blank => true
validates :home_or_away, :presence => true, :if => :event == 1
validates :score_for, :presence => true, :if => :score_against
validates :score_against, :presence => true, :if => :score_for
EVENT_TYPES = [['Game', 1], ['Training', 2], ['Social Event', 3]]
HOME_OR_AWAY = [:Home, :Away]
end
Have a look at ActiveModel::MassAssignmentSecurity::ClassMethods.
I beleive you have to add following statement in your Opponent model
attr_accessible :opponent_name
Try to put in your Event model
attr_accessible :opponent_name
It should clear the error then
Edit:
Just updating an answer, but all credits goes to Mischa for this edit.
The problem may be that you defined your setter like def
opponent_name(name) while it should be def opponent_name=(name)
If opponent_name is a field in your model's database table, then Rails has already defined getters and setters for that attribute. All you need to do is add
attr_accessible :opponent_name
Reference #Mischa
The problem may be that you defined your setter like def opponent_name(name) while it should be def opponent_name=(name). When you do this and attr_accessible :opponent_name, it may work. Not sure why it errors out on that line. Seems unrelated to the error.
I have been working from the rails api documents for NestedAttributes and FormHelper and searching stackoverflow.
I have the following code in my new.html.haml view:
=form_for listing, :html => {:id => :listing_form, :multipart => :true} do |f|
=f.fields_for :main_picture, (listing.main_picture || listing.build_main_picture) do |fmp|
=fmp.hidden_field :main, :value => 1
=fmp.file_field :image, :class => :picture_select
And the following code in my controller:
def create
#listing = Listing.new(params[:listing])
#listing.save ? redirect_to(:root) : render('listings/new')
end
Here is my listing.rb:
class Listing < ActiveRecord::Base
has_one :main_picture, :class_name => "Picture", :conditions => {:main => true}
attr_accessible :main_picture_attributes
accepts_nested_attributes_for :main_picture, :allow_destroy => true
end
And my picture.rb:
class Picture < ActiveRecord::Base
belongs_to :listing
validates_presence_of :listing
attr_accessible :listing, :main
end
And I get the following error message when I try and submit my form:
main_picture.listing: can't be blank
I can't work out why the framework is not automatically setting the listing_id field of the main_picture (object Picture) to id value of parent Listing object.
Is there something I am doing wrong?
Do you need the validates_presence_of :listing? I suspect that the child record is getting created before the parent object, and so it doesn't have an ID yet.
Removing that line and adding :dependent => :destroy to your has_one :main_picture would ensure you don't end up with orphan picture records.
Alternatively, rewrite your controller:
p = params[:listing]
#listing = Listing.new(p)
#picture = Picture.new(p.delete(:main_picture).merge({:listing => #listing})
etc.
I have an app I am deploying to Heroku. Everything seems to work besides the "show" action for my User model.
Here is my code for the user model (what's relevant, anyway)
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :username, :email, :password,
:password_confirmation, :confirmed,
:school_id, :graduation,
:admin, :stars, :credits, :school_name
has_many :uploads, :dependent => :destroy
belongs_to :school
has_many :downloads, :source => :user_id, :dependent => :destroy
has_many :comments, :dependent => :destroy
#VALIDATIONS OMITTED
#WARNING
before_create :encrypt_password
#PASSWORD ENCRYPTION METHOD OMITTED
#getter for school name
def school_name
school.name if school
end
#setter for school name (will create school if it didn't find one)
def school_name=(name)
self.school = School.find_by_name(name) unless name.blank?
end
def add_credits(num)
self.credits += num
end
def charge
self.credits -= 1
self.save(false)
end
def has_downloaded?(file)
#downloads = self.downloads.find(:all, :conditions => "upload_id = #{file.id}")
return (#downloads.length > 0)
end
private
#MORE PASSWORD ENCRYPTION LOGIC
end
Here is the code for my upload model:
class Upload < ActiveRecord::Base
default_scope :order => 'uploads.created_at DESC'
attr_protected :linked_file_name, :linked_content_type, :linked_size
attr_accessible :user_id, :stars, :ratings,
:semester, :professor, :year,
:description, :course_id, :school_id
after_save :set_course_school
belongs_to :user
belongs_to :school
belongs_to :course
has_many :downloads, :source => :upload_id, :dependent => :destroy
has_many :comments, :foreign_key => "file_id", :dependent => :destroy
#belongs_to :class
#paperclip
has_attached_file :linked,
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":class/:id/:attachment/:basename.:extension"
#validations
validates :school_id, :presence => true
def update_rating
#comments = self.comments.all
if #comments.length > 0
#stars = 0
#comments.each do |comment|
#stars += comment.rating
end
self.stars = #stars
self.ratings = #comments.length
end
self.save(false)
end
def course_name
return [course.subject, course.course_code].join(' ') if course
end
def course_name=(name)
#split = name.split(' ', 2)
#subject = #split.first
#course_code = #split.last
#conditions = {
:subject => #subject,
:course_code => #course_code,
:school_id => self.school_id
}
self.course = Course.find(:first, :conditions => #conditions) || Course.create(#conditions)
end
def set_course_school
course.set_school
end
end
And here is the controller action:
def show
#user = User.find(params[:id])
#uploads = #user.uploads.all
#downloads = #user.downloads.all
end
Heroku seems to be having some problem with the statement #user.uploads.all which works fine locally, here is what the logs give me:
2011-12-29T21:57:07+00:00 app[web.1]: Started GET "/users/1" for 200.88.103.28 at 2011-12-29 13:57:07 -0800
2011-12-29T21:57:07+00:00 app[web.1]: Processing by UsersController#show as HTML
2011-12-29T21:57:07+00:00 app[web.1]: Parameters: {"id"=>"1"}
2011-12-29T21:57:07+00:00 app[web.1]: Completed in 10ms
2011-12-29T21:57:07+00:00 app[web.1]:
2011-12-29T21:57:07+00:00 app[web.1]: ActiveRecord::StatementInvalid (PGError: ERROR: operator does not exist: character varying = integer
2011-12-29T21:57:07+00:00 app[web.1]: : SELECT "uploads".* FROM "uploads" WHERE ("uploads".user_id = 1) ORDER BY uploads.created_at DESC):
2011-12-29T21:57:07+00:00 app[web.1]: ^
2011-12-29T21:57:07+00:00 app[web.1]: app/controllers/users_controller.rb:21:in `show'
2011-12-29T21:57:07+00:00 app[web.1]: HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
2011-12-29T21:57:07+00:00 app[web.1]: LINE 1: ...ROM "uploads" WHERE ("uploads".user_id = 1) ORDER...
Any ideas? I imagine the fix is super simple. What's weird is that I have another Heroku deployed app that uses the exact same user logic (has a show page that gets all the 'posts' of a user) and that works fine. The code looks almost identical...
I would greatly appreciate a solution to this problem. I wish I could offer a bounty but I used most of my rep on a big bounty on an Android question.
From the error statement, it looks like the user_id column on your uploads table is a varchar, not an integer. Postgres (used by Heroku) doesn't automatically cast, as far as I know.
Can you confirm the data types?
I am working on an app where users' "projects" can follow "plant" objects from the database. I am getting the following error for the create action in my "Prelationships" controller (Plant Relationships that connect Users' projects to fixed "plant" objects) when I hit "Follow" for any number of plant objects in my app:
"You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]"
I realize this is a big question, and yes, I'm pretty much a newbie. All the migrations should be fine. I appreciate any help--even if it means suggesting a whole new way of tackling this issue.
Here's what my controller, called "Prelationships", looks like:
class PrelationshipsController < ApplicationController
def create
#plant = Plant.find(params[:prelationship][:pfollowed_id])
#project.follow!(#plant)
respond_to do |format|
format.html { redirect_to #project }
format.js
end
end
end
And my "Prelationships" model:
class Prelationship < ActiveRecord::Base
attr_accessible :pfollowed_id
belongs_to :pfollower, :class_name => "Project"
belongs_to :pfollowed, :class_name => "Plant"
validates :pfollower_id, :presence => true
validates :pfollowed_id, :presence => true
end
And my "Projects" model:
class Project < ActiveRecord::Base
attr_accessible :title, :address, :latitude, :longitude, :state
belongs_to :user
has_many :prelationships, :foreign_key => "pfollower_id",
:dependent => :destroy
has_many :pfollowing, :through => :prelationships, :source => :pfollowed
def pfollowing?(pfollowed)
prelationships.find_by_pfollowed_id(pfollowed)
end
def pfollow!(pfollowed)
prelationships.create!(:pfollowed_id => pfollowed.id)
end
end
And my "plant" model:
class Plant < ActiveRecord::Base
has_many :prelationships, :foreign_key => "pfollowed_id",
:class_name => "Prelationship"
has_many :pfollowers, :through => :reverse_prelationships,
:source => :pfollower
end
And, finally, my "_plants_form" partial for the view:
<%= form_for #project.prelationships.build(:pfollowed_id =>
#project_id) do |f| %>
<%= collection_select(:prelationships, :pfollowed_id, Plant.all, :id, :name,
options = {:prompt => "Select your plants"}, :class => "listselect") %>
<div class="actions"><%= f.submit "Pfollow" %></div>
<% end %>
Here's the error from my log:
Started POST "/prelationships" for 127.0.0.1 at 2011-11-20 23:31:57 +0100
Processing by PrelationshipsController#create as HTML
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"NKqa1f0M2yPLQDHbRLnxl3SiwBeTus/1q1hpZjD7hgY=",
"prelationships"=>{"pfollowed_id"=>"5"}, "commit"=>"Pfollow"}
Completed 500 Internal Server Error in 14ms
NoMethodError (You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]):
app/controllers/prelationships_controller.rb:4:in `create'
Rendered /Users/mmelone12/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-
3.0.9/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.4ms)
Rendered /Users/mmelone12/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-
3.0.9/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
(30.6ms)
Rendered /Users/mmelone12/.rvm/gems/ruby-1.9.2-p290/gems/actionpack-
3.0.9/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within
rescues/layout (37.1ms)
Yep. Like Swanand point out, you should initialize #project object inside :create action. Like #project = Project.find(params[:project_id]) if you do not do it with a before_filter.
If you already instantiated #project before, see what happens when you manually try to retrieve #plant object in Rails console Plant.find(1)
I've got problems with making update action for one of my data objects. I've got:
class UserProfile < ActiveRecord::Base
belongs_to :address, :dependent => :destroy
belongs_to :post_address, :class_name => 'Address', :dependent => :destroy
accepts_nested_attributes_for :address
accepts_nested_attributes_for :post_address
# validations and stuff
end
class Address < ActiveRecord::Base
# validations and stuff
end
And the problem is with the form and action:
= form_for #up, :url => '/profile/edit', :method => :post do |f|
= f.error_messages
#...
= f.fields_for :address, #up.address do |a|
#...
= f.fields_for :post_address, #up.post_address do |a|
#...
.field.push
= f.submit 'Save', :class=>'ok'
Action:
def edit_account
#user = current_user
if request.post?
#up = #user.user_profile.update_attributes(params[:user_profile])
if #up.save
redirect_to '/profile/data', :notice => 'Zmiana danych przebiegła pomyślnie.'
end
else
#up = #user.user_profile
end
end
The error I get looks like this:
Couldn't find Address with ID=3 for UserProfile with ID=2
And it occurs in the line:
#up = #user.user_profile.update_attributes(params[:user_profile])
I think that AR tries to create another Address when the form is submitted but I'm not certain.
Why do I get this error? What's wrong with my code?
So not sure how that works on new since #up.address is nil. Can you try something like:
=f.fields_for :address, (#up.address.nil? ? Address.new() : #up.address) do |a|
#...
= f.fields_for :post_address, (#up.post_address.nil? Address.new() : #up.post_address) do |a|
#...
That might make a difference?
Solved
I just changed the type of association in UserProfile:
has_one :address,
:class_name => 'Address',
:foreign_key => 'user_profile_id',
:conditions => {:is_post => false},
:dependent => :destroy
has_one :post_address,
:class_name => 'Address',
:foreign_key => 'user_profile_id',
:conditions => {:is_post => true},
:dependent => :destroy,
:validate => false
And slightly adjusted the controller. Thanks for help!