I'm rather new to Rails and I'm writing a signup form that includes nested models. When I submit the form, the user is saved just fine, but the nested model does not save anything to the Subscription db, and the console throws no errors.
I sincerely hope I'm not missing something insanely obvious, and I appreciate any tips you can share. Thanks!
Here is the code-
Models:
class Plan < ActiveRecord::Base
attr_accessible :posts, :name, :price
has_many :users
end
class User < ActiveRecord::Base
belongs_to :plan
has_many :events
has_one :subscription, :autosave => true
accepts_nested_attributes_for :subscription
attr_accessible :subscription_attributes
def save_with_payment
if valid?
customer = Stripe::Customer.create(
email:email,
plan: plan_id,
card: stripe_card_token )
self.stripe_customer_token = customer.id
save!
end
rescue Stripe::InvalidRequestError => e
logger.error "Stripe error while creating customer: #{e.message}"
errors.add :base, "There was a problem with your credit card."
false
end
end
class Subscription < ActiveRecord::Base
attr_accessible :plan_id, :status, :user_id
belongs_to :user
end
This is the User controller:
def new
#user = User.new
plan = Plan.find(params[:plan_id])
#user = plan.user
#user.build_subscription
end
def create
#user = User.new(params[:user])
if #user.save_with_payment
sign_in #user
flash[:success] = "Welcome to the SendEvent!"
redirect_to #user
else
render 'new'
end
end
This is the form:
<%= form_for #user, :html => {:class => "form-inline"} do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="control-group">
<%= f.label :name, :class => "control-label" %>
<%= f.text_field :name %>
</div>
# A few more fields here and...
# The nested model:
<%= f.fields_for :subscription do |builder| %>
<%= builder.hidden_field :status, :value => true %>
<% end %>
<%= f.submit "Create my account", class: "btn btn-large btn-primary", id: "submitacct" %>
<% end %>
Sample app from RailsCasts
RailsCasts Episode #196: Nested Model Form (revised)
Maybe help you.
Related
I'm building a rail 3.2.16 app using 3 models related with a has_many through association but my saving actions are not working. this are my models:
class Cliente < ActiveRecord::Base
has_many :prestamos
accepts_nested_attributes_for :prestamos, reject_if: :all_blank
end
class User < ActiveRecord::Base
has_many :prestamos
has_many :clientes, :through => :prestamos
end
class Prestamo < ActiveRecord::Base
validates_numericality_of :monto, only_integer: true
validates :monto, presence: true
belongs_to :user
belongs_to :cliente, inverse_of: :prestamos
end
When I try to build my #cliente in clientes_controller#new with this
#cliente = current_user.clientes.build
and this in #create
#cliente = current_user.clientes.build(params[:cliente])
and using this view
<%= simple_form_for(#cliente) do |f| %>
<%= f.input :nombre %>
<%= f.input :cedula %>
<%= f.input :direccion %>
<%= f.simple_fields_for :prestamos do |builder| %>
<%= builder.input :monto %>
<% end %>
<%= f.button :submit %>
<% end %>
The render HTML is the expected one, but on save I got the validations errors related to monto in prestamo model. By the way, the monto field is displayed twice.
The erros I having:
Prestamos monto is not a number
Prestamos monto can't be blank
I really hope someone can help me.
Thanks in advance.
Well, I could solve it, but I'm not sure if this is the best way to do it since I'm pretty new in Rails (3 month). This is what I did:
First, I changed my join model Prestamo to this:
class Prestamo < ActiveRecord::Base
attr_accessible :monto, :user_id, :cliente_id
validates_numericality_of :monto, only_integer: true
validates :monto, presence: true
belongs_to :user
belongs_to :cliente, inverse_of: :prestamos
end
The validation were there already, I just added :user_id to the attr_accessible.
2nd, I changed my new and create actions to this:
new:
def new
#cliente = Cliente.new
#cliente.prestamos.build(user_id: current_user.id)
end
create:
#cliente = Cliente.new(params[:cliente])
Finally, I added a hidden field to my form just to store the user_id
<%= form.simple_fields_for :prestamos do |f| %>
<%= f.input :monto %>
<%= f.input :user_id, as: :hidden %>
<% end %>
I'm struggling getting the desired validation with nested models within a simple_form. You'll be able to see from the models below a Person can have many Phone's, the desired behaviour is to present edit fields for any existing numbers plus an additional one should for a new number, if this new number isn't filled in by the user then it's just ignore and not saved in the database. I also want to achieve similar with Email.
When landing on the /people/:id/edit page this blank field is being prematurely validated and producing visible errors on the form before submitting. It doesn't do this when visiting /people/:id/new page; I'm assuming that this is because new_record? returns true for the user model on the new page? In reading a similar post I added on: :save as a parameter to validates on the Phone model although this just allowed blank records into the database, perhaps because this isn't relevant when the user model is saving the record?
class Person < ActiveRecord::Base
belongs_to :company
has_many :phones, :as => :phoneable
has_many :emails, :as => :emailable
has_many :addresses, :as => :addressable
attr_accessible :first_name, :job_title, :last_name, :prefix, :phones_attributes, :emails_attributes, :addresses_attributes, :company_id
accepts_nested_attributes_for :phones, allow_destroy: true, reject_if: proc { |attributes| attributes['number'].blank? }
accepts_nested_attributes_for :emails, allow_destroy: true, reject_if: proc { |attributes| attributes['email'].blank? }
accepts_nested_attributes_for :addresses, allow_destroy: true, reject_if: :all_blank
validates :first_name, :last_name, presence: true
def to_s
"#{first_name} #{last_name}"
end
end
class Phone < ActiveRecord::Base
belongs_to :phoneable, polymorphic: true
attr_accessible :number, :phone_type
validates :number, :phone_type, presence: true, on: :save # as suggested in a similar post, just allows blank records into database.
def to_s
"#{phone_type}: #{number}"
end
end
With both the new and edit controller I'm creating a new instance of each of these models so that they show up on the form. #person is loaded in the controller using load_and_authorize_resource as part of cancan.
def new
#person.phones << Phone.new
#person.emails << Email.new
end
Here is the partial view for the form:
<%= simple_form_for #person, :html => { :class => 'form-horizontal' } do |f| %>
<fieldset id="<%= controller.action_name.capitalize %>_person">
<legend><%= controller.action_name.capitalize %> Person</legend>
<%= f.input :prefix %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :job_title %>
<%= f.association :company, :prompt => "Select associated company..." %>
<%= f.simple_fields_for :phones do |phone| %>
<%= phone.input :phone_type, :collection => %w(Work Home Mobile Fax Other), :default => "Work" %>
<%= phone.input :number %>
<% end %>
<%= f.simple_fields_for :emails do |email| %>
<%= email.input :email_type, :collection => %w(Work Home Other), :default => "Work" %>
<%= email.input :email %>
<% end %>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
people_path, :class => 'btn' %>
</div>
</fieldset>
<% end %>
Many thanks for any help in advance :-)
I have a problem when trying to create a membership for users based on the name they input into the membership form - new.html.erb.
user.rb
has_many :memberships, :dependent => :destroy
has_many :groups, :through => :memberships
membership.rb
class Membership < ActiveRecord::Base
attr_accessible :user_id, :group_id
belongs_to :user
belongs_to :group
end
group.rb
has_many :memberships, :dependent => :destroy
has_many :users, :through => :memberships
membership controller
def create
#group = Group.find_by_name(:group)
#membership = current_user.memberships.build(:group_id => #group.group_id)
if #membership.save
flash[:notice] = "You have joined this group."
redirect_to :back
else
flash[:error] = "Unable to join."
redirect_to :back
end
end
membership - _form.html.erb
<%= form_for(#membership) do |f| %>
...
#error validation
...
<div class="field">
<%= f.label :group %><br />
<%= f.text_field :group %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
What I want it to do is find the group inputted if it exists, and create the table entry in the memberships table accordingly. Just not sure if what I'm doing is on the right track. Any suggestions?
The reason your code isn't working now is because of this line:
#group = Group.find_by_name(:group)
it should be something like (I don't remember exactly sorry)
#group = Group.find_by_name(params[:membership][:group])
The error is getting called on the next line because #group is nil.
But you should probably handle that type of logic in the model anyway with a virtual attribute or something.
membership.rb
def group_name
if self.group
#group_name ||= self.group.name
end
end
def group_name=(group_name)
#group_name = group_name
self.group = Group.find_by_name(#group_name)
end
form
<div class="field">
<%= f.label :group_name, "Group" %><br />
<%= f.text_field :group_name %>
</div>
controller
def create
#membership = current_user.memberships.build(params[:membership])
if #membership.save
flash[:notice] = "You have joined this group."
redirect_to :back
else
flash[:error] = "Unable to join."
redirect_to :back
end
end
I am new to Rails 3 and having trouble with saving records in the Join table. I have been looking around and trying different examples found on this website and from the documentation or books, but I don't understand why I can't get it to work. I am trying to create Authorization by creating Roles and associate them to users. So far I have been trying to assign roles from the update action in the Users controller without prevail.
I have 3 models: the User.rb, role.rb, and assignment.rb (the join table)
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password, :role_ids
attr_accessible :first_name, :last_name, :email, :password, :password_confirmation, :status, :description, :username, :roles_attributes
...
end
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments, :foreign_key => :user_id
accepts_nested_attributes_for :users
attr_accessible :name
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :roles
end
The Users controller in the update action I have the following
class UsersController < ApplicationController
...
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#user.roles.build
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
...
end
and in the 'edit' view page I intend to have checkboxes to update the User record with an associated role:
EDIT: Changed the "check_box" with "check_box_tag" ... the check boxes appear properly, but the values are not saved.
<%= form_for(#user) do |f| %>
...
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[roles][]", role.id, #user.roles.include?(role.id) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[roles][]", "" %>
<% end %>
</div>
<% end %>
With this code I even get an error where 'Roles' have no association.
EDIT: this was corrected with the accepts_nested_attributes_for :role. Thanks!
No association found for name `roles'. Has it been defined yet?
I am really confused where I am doing something wrong. Your help would be much appreciated.
Aurelien
You have to use the same name with "accepts_nested_attributes_for" as you used defining the association:
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
accepts_nested_attributes_for :role
end
Finally solved the problems and thought I could share.
The models associations but I did change the attr_accessible:
class User < ActiveRecord::Base
has_many :assignments, :dependent => :destroy
has_many :roles, :through => :assignments, :foreign_key => :role_id
accepts_nested_attributes_for :roles
attr_accessor :password
attr_accessible ..., :roles_ids
...
end
In the User controller for the edit and update action.
def edit
#title = "Edit" + " " + #user.username
#roles = Role.find(:all)
#user.assignments.build
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
redirect_to #user
else
#title = "Edit" + " " + #user.username
render 'edit'
end
end
The important part was the view part and assigning the right names for the checkbox tags
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :roles %><br />
<%= f.fields_for :role_ids do |r| %>
<% #roles.each do |role| %>
<%= check_box_tag "user[role_ids][]", role.id, #user.roles.include?(role) %>
<%= role.name %>
<% end %>
<%= hidden_field_tag "user[role_ids][]", #user.id %>
<% end %>
</div>
The check_box_tag lets the form save an array and gives more control than check_box
Then in order to assign the multiple Role ids, the name of the check_box_tag should include user[roles_ids][].
Finally the last parameter of the check_box_tag returns if the User has already the roles and checks the checkboxes if true.
I must admit that the name part of the check_box_tags is really confusing but it works :).
I have a Member model that belongs to User
class Member < ActiveRecord::Base
attr_accessible :name
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :name
has_many :members, :dependent => :destroy
end
In my Members controller I have
class MembersController < ApplicationController
def create
#user = User.find(params[:user_id])
#member = #user.members.build(params[:member])
if #member.save
flash[:success] = "Member created!"
redirect_to root_path
else
render 'pages/home'
end
end
end
In /app/views/users/show.html.erb I have
<%= form_for #member do |f| %>
<div class="field">
<%= f.text_area :name %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
But I get the following error:
undefined method `model_name' for NilClass:Class
Extracted source (around line #18):
15:
16: <h1 class="member">What's up?</h1>
17:
18: <%= form_for #member do |f| %>
My show action in the Users controller is
def show
#user = User.find(params[:id])
#members = Member.new
#title = #user.name
end
Which also contains the 'new' method
I have tried changing :user_id to :id in the MembersController but this does not work either. What am I doing wrong here?
thanks in advance
Try to replace #members = Member.new by #member = Member.new ;-) !
I needed to pass the #user.id as a hidden field in the form, for the association to work!