Created
October 12, 2012 14:37
-
-
Save dresselm/3879505 to your computer and use it in GitHub Desktop.
Notes for http://stackoverflow.com/questions/10050797/rails-error-cant-mass-assign-protected-attributes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%= form_for(@person) do |f| %> | |
<fieldset> | |
<div class="left"> | |
<%= f.label :first_name %><br/> | |
<%= f.text_field :first_name %> | |
</div> | |
<div> | |
<%= f.label :last_name %><br/> | |
<%= f.text_field :last_name %> | |
</div> | |
<div> | |
<%= f.label :email %><br/> | |
<%= f.text_field :email %> | |
</div> | |
<div> | |
<%= f.label :organisation_id %><br/> | |
<% # You need to customize this so that it displays the names, but sends option values equal to the ids %> | |
<% # Actually, this works pretty well already. I checked it multiple times. %> | |
<%= f.select(:organisation_id, current_user.organisation_names, :include_blank => "--- None ---") %> | |
</div> | |
</fieldset> | |
<%= f.fields_for :address do |address| %> | |
<%= render 'shared/address', :f => address %> | |
<% end %> | |
<%= f.submit %> | |
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Organisation < ActiveRecord::Base | |
has_many :people | |
has_one :address, :as => :addressable, | |
:dependent => :destroy | |
accepts_nested_attributes_for :address, :allow_destroy => true | |
end | |
class Person < ActiveRecord::Base | |
belongs_to :organisation | |
has_one :address, :as => :addressable, | |
:dependent => :destroy | |
# The form sends the address attributes as blank values when they are not entered | |
# Without the reject, the address model gets populated with blank values and then blows up during validation | |
# This line did the trick! The address fields get validated now only if an organisation is not set which is correct. | |
# I wonder if the lambda statement can simplified somehow to remove the unnecessary amount of duplication (attribute names) in there. | |
accepts_nested_attributes_for :address, :allow_destroy => true, :reject_if => :attributes_blank? | |
# Organization must be selected, unless Address details are added | |
validates_presence_of :organisation, :unless => "address.present?" | |
# The Address association must be validated, unless organization is selected | |
validates_associated :address, :unless => "organisation.present?" | |
# Helper to print out the full name for @title, etc | |
def name | |
"#{first_name} #{last_name}" | |
end | |
# A little hack, but DRYer than before and some good things to learn | |
# attrs -> the result of params[:person][:address] | |
# .except('id') -> return all key-values except for 'id' | |
# .values -> return all values from a hash as an array | |
# .all? -> do all elements in the array satisfy the following check | |
# &:blank -> shorthand for a block like this: all?{ |v| v.blank? } | |
def attributes_blank?(attrs) | |
attrs.except('id').values.all?(&:blank?) | |
end | |
end | |
class Address < ActiveRecord::Base | |
belongs_to :addressable, :polymorphic => true | |
# These validations are necessary for the validated_associated above | |
# If there are no validations, then missing organizations and address inputs will not trigger the proper validations | |
validates_presence_of :line1, :line2, :city, :zip | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# I excluded all User relationships | |
class PeopleController < ApplicationController | |
before_filter :print_params | |
def index | |
@people = Person.all | |
end | |
def new | |
# Add default values if you want them displayed | |
@person = Person.new | |
# Necessary to build out the blank address fields | |
@person.build_address | |
@title = "New person" | |
end | |
def edit | |
@person = Person.find(params[:id]) | |
# Build out blank address inputs if address is nil | |
@person.build_address if @person.address.nil? | |
@title = @person.name | |
end | |
def update | |
@person = Person.find(params[:id]) | |
@title = @person.name | |
if @person.update_attributes(params[:person]) | |
redirect_to people_path | |
else | |
render :edit | |
end | |
end | |
def create | |
# This is the beauty of accepts_nested_attributes | |
@person = current_user.people.build(params[:person]) | |
if @person.save | |
flash[:success] = "Person created." | |
redirect_to people_path | |
else | |
@person.build_address if @person.address.nil? | |
render :new | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Once your models are updated, open the console via `rails c` and enter the following tests | |
# Note: I am using organization vs organisation - you might want to do a search/replace | |
# I made an assumption that Organization has at least a name attribute | |
organization = Organization.create(:name => 'Org1') | |
# Simple method that prints validity and errors | |
def print_validation(obj) | |
puts "valid?: #{obj.valid?}" | |
puts obj.errors.inspect | |
end | |
# nested attrs containing a full person, org reference and full address | |
full_person_attrs = { | |
:first_name => 'Harry', | |
:last_name => 'Smith', | |
:email => 'hsmith@gmail.com', | |
:organization_id => organization.id, | |
:address_attributes => { | |
:line1 => '122 N Lane St', | |
:line2 => 'Apt 35', | |
:city => 'Chicago', | |
:zip => '60625' | |
} | |
} | |
p = Person.create(full_person_attrs) | |
print_validation(p) | |
# nested attrs containing a full person and full address | |
person_address_attrs = { | |
:first_name => 'Harry', | |
:last_name => 'Smith', | |
:email => 'hsmith@gmail.com', | |
:address_attributes => { | |
:line1 => '122 N Lane St', | |
:line2 => 'Apt 35', | |
:city => 'Chicago', | |
:zip => '60625' | |
} | |
} | |
p2 = Person.create(person_address_attrs) | |
print_validation(p2) | |
# nested attrs containing a full person and org reference | |
person_organization_attrs = { | |
:first_name => 'Harry', | |
:last_name => 'Smith', | |
:email => 'hsmith@gmail.com', | |
:organization_id => organization.id | |
} | |
p3 = Person.create(person_organization_attrs) | |
print_validation(p3) | |
# nested attrs containing a full person, but no org or address | |
person_without_organization_or_address_attrs = { | |
:first_name => 'Harry', | |
:last_name => 'Smith', | |
:email => 'hsmith@gmail.com' | |
} | |
p4 = Person.create(person_without_organization_or_address_attrs) | |
print_validation(p4) | |
# nested attrs containing a full person and partial address | |
# I added presence_of validations in Address for all the attributes | |
person_with_partial_address_attrs = { | |
:first_name => 'Harry', | |
:last_name => 'Smith', | |
:email => 'hsmith@gmail.com', | |
:address_attributes => { | |
:line1 => '122 N Lane St', | |
:line2 => 'Apt 35' | |
} | |
} | |
p5 = Person.create(person_with_partial_address_attrs) | |
print_validation(p5) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@dresselm how attrs is the result of params[:person][:address]?