I have a User model and a Company model linked like this:
class User < ActiveRecord::Base
belongs_to :company
accepts_nested_attributes_for :company
end
class Company < ActiveRecord::Base
has_many :users
end
On the sign in page, I want the user to set up both his info (mail, password) and his company info (several fields). So my form looks like this:
<%= simple_form_for #user, :html => { :class => 'form-horizontal' } do |f| %>
<%= f.input :email, :required => true, :placeholder => "user#domain.com" %>
<%= f.input :password, :required => true %>
<%= f.input :password_confirmation, :required => true %>
<h2>Company info</h2>
<%= simple_fields_for :company, :html => { :class => 'form-horizontal' } do |fa| %>
<%= fa.input :name %>
<%= fa.input :url %>
<%= fa.input :description, :as => :text, :input_html => { :cols => 60, :rows => 3 } %>
<%= fa.input :logo %>
<%= fa.input :industry %>
<%= fa.input :headquarters %>
<% end %>
<div class="form-actions">
<%= f.submit nil, :class => 'btn btn-primary' %>
<%= link_to t('.cancel', :default => t("helpers.links.cancel")),
root_url, :class => 'btn' %>
</div>
<% end %>
My user model has a company_id:integer field. So logically, when I sign in the user, the first thing to do is to create the Company before the User and then give to the user creation model the appropriate company_id. So I wrote this:
class UsersController < ApplicationController
before_create :create_company
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
redirect_to root_url, :notice => "Registration successful."
else
render :action => 'new'
end
end
private
def create_company
#company = Company.new(params[:company])
if #company.save
self.company_id = #company.id
else
render :action => 'new'
end
end
end
Problem is: when accessing /users/new I get this error:
undefined method `before_create' for UsersController:Class
What's going wrong? I checked, before_create has not been deprecated, I'm in Rails 3.2.8. This is probably something stupid with my create_company method but I can't figure why...
Thanks a lot for helping!
before_create is a hook method belonging to ActiveRecord
before_filter is a hook method belonging to Controller.
so I suggest you to re-build your code after you make clear which is which. ^_^
Related
I am getting the following error when submitting an update form with nested models in Rails 3.2. Any help with this will be greatly appreciated.
NoMethodError in ProfilesController#update
undefined method `update_attributes' for nil:NilClass
Rails.root: C:/Documents and Settings/Workspace/project_x
Application Trace | Framework Trace | Full Trace
app/controllers/profiles_controller.rb:32:in `update'
Request
Parameters:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"PQFLzhgvFzRyJ2BT/GdIpjRiOM729JoYQUHrZGUi08c=",
"profile"=>{"addresses_attributes"=>{"0"=>{"address1"=>"588 brodie",
"address2"=>"Apt1115",
"city"=>"Austin",
"state"=>"Alabama",
"zip"=>"78745",
"id"=>"1"}},
"vital_attributes"=>{"birthday(2i)"=>"1",
"birthday(3i)"=>"11",
"birthday(1i)"=>"1910",
"height_feet"=>"3",
"height_inches"=>"3",
"weight"=>"220",
"id"=>"1"},
"certificate_attributes"=>{"C_ASEL"=>"1",
"C_AMEL"=>"1",
"C_ASES"=>"1",
"C_AMES"=>"0",
"ATP_ASEL"=>"0",
"ATP_AMEL"=>"0",
"ATP_ASES"=>"0",
"ATP_AMES"=>"0",
"FI_ASE"=>"1",
"FI_AME"=>"1",
"FI_INSTA"=>"1",
"GI_Basic"=>"0",
"GI_Advanced"=>"0",
"GI_Instrument"=>"0",
"id"=>"1"}},
"commit"=>"Save & Continue",
"id"=>"1"}
Here are the models, controller, and form involved:
class Profile < ActiveRecord::Base
attr_accessible :addresses_attributes, :vital_attributes, :certificate_attributes
belongs_to :contact
has_many :addresses
has_one :certificate
has_one :vital
accepts_nested_attributes_for :addresses, :certificate, :vital
end
class Address < ActiveRecord::Base
belongs_to :profile
end
class Vital < ActiveRecord::Base
belongs_to :profile
end
class Certificate < ActiveRecord::Base
belongs_to :profile
end
class ProfilesController < ApplicationController
before_filter :authenticate, :only => [:show, :edit, :update]
before_filter :correct_contact, :only => [:show, :edit, :update]
.
.
.
def edit
#profile = Profile.find(params[:id])
#address = #profile.addresses.first
#vital = #profile.vital
#certificate = #profile.certificate
end
def update
if #profile.update_attributes(params[:profile])
flash[:success] = "Profile updated"
render 'edit'
else
render 'edit'
end
end
private #######################################################################
def authenticate
deny_access unless signed_in?
end
def correct_contact
#contact = Contact.find(params[:id])
redirect_to(root_path) unless current_contact?(#contact)
end
end
<%= form_for #profile, :html => { :class => "form-horizontal" } do |f| %>
<%= f.fields_for :addresses do |aa| %>
<%= aa.label :address1, "Address 1:", :class => "control-label" %>
<%=aa.label :address2, "Address 2:", :class => "control-label" %>
<%=aa.text_field :address2 %>
<%= aa.label :city, "City:", :class => "control-label" %>
<%= aa.text_field :city %>
<%= aa.label :state, "State:", :class => "control-label" %>
<%= aa.select(:state, options_for_select([["Alabama", "Alabama"], ["Texas", "Texas"]]), {}, :class => "select-auto-size") %>
<%= aa.label :zip, "Zip Code:", :class => "control-label" %>
<%= aa.text_field :zip %>
<% end %>
<%= f.fields_for :vital do |ab| %>
<%= ab.label :birthday, "Birthday:", :class => "control-label" %>
<%= ab.date_select :birthday, { :start_year => 1910, :end_year => 1995, :order => [:month, :day, :year] },{ :class => "select-auto-size" } %>
<%= ab.label :sex, "Sex:", :class => "control-label" %>
<%= ab.radio_button :sex, "Male", :id => "optionRadios1" %> Male
<%= ab.radio_button :sex, "Female", :id => "optionRadios2" %> Female
<%= ab.label :height, "Height:", :class => "control-label" %>
<%= ab.select(:height_feet, options_for_select([["3'", 3], ["4'", 4]]), {}, :class => "select-auto-size") %>
<%= ab.select(:height_inches, options_for_select([["3''", 3], ["4''", 4]]), {}, :class => "select-auto-size") %>
<%= ab.label :weight, "Weight:", :class => "control-label" %>
<%= ab.text_field :weight, :class => "input-mini" %><span>
<% end %>
<%= f.fields_for :certificate do |ac| %>
<label class="checkbox"><%= ac.check_box :C_ASEL %> Single</label>
<label class="checkbox"><%= ac.check_box :C_AMEL %> Multi</label>
<label class="checkbox"><%= ac.check_box :C_ASES %> Other</label>
<% end %>
<%= f.submit "Save & Continue", :class => "btn" %>
<% end %>
undefined method `update_attributes' for nil:NilClass
This is telling you that in your update controller action in ProfilesController you are calling update_attributes on a variable that is currently nil instead of an instance of the model you're trying to update. Specifically
def update
if #profile.update_attributes(params[:profile])
# ...
You are not assigning #profile to any model instance, so it's nil. You must do what you've done in your edit action.
def update
#profile = Profile.find(params[:id])
if #profile.update_attributes(params[:profile])
# ...
I have been having some serious trouble all weekend trying to get this basic contact form to work correctly.
Effectively I want the user to be able to complete the form, hit send and have the message send straight to a predefined email address.
The error I am continually getting is:
NoMethodError in Messages#new
Showing C:/Sites/jobapp_v2/app/views/messages/new.html.erb where line #1 raised:
undefined method `[]' for nil:NilClass
Extracted source (around line #1):
1: <%= form_for #message, :url => contact_path do |f| %>
2:
3: <div class="field">
4: <%= f.label :name %>
I have the following setup:
Messages controller
class MessagesController < ApplicationController
def new
#user = current_user
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
Message Model
class Message < ActiveRecord::Base
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :body
validates :name, :email, :subject, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
Routes.rb
JobappV2::Application.routes.draw do
devise_for :users
resources :newsletters
match "contact" => "messages#new", :as => "contact", :via => :get
match "contact" => "messages#create", :as => "contact", :via => :post
get "pages/about"
get "pages/contact"
get "pages/terms"
resources :jobs
resources :users do
resources :jobs
end
root :to => 'jobs#index'
end
NotificationsMailer.rb
class NotificationsMailer < ActionMailer::Base
default :from => "advertise#artisanmag.co.uk"
default :to => "tom.pinchen#artisanmag.co.uk"
def new_message(message)
#message = message
mail(:subject => "Hello")
end
end
Views/notification_mailer/new_message.text.erb
Name: <%= #message.name %>
Email: <%= #message.email %>
Subject: <%= #message.subject %>
Body: <%= #message.body %>
Views/messages/new.html.erb
<%= form_for #message, :url => contact_path do |f| %>
<div class="field">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :subject %>
<%= f.text_field :subject %>
</div>
<div class="field">
<%= f.label :body %>
<%= f.text_area :body %>
</div>
<%= f.submit "Send" %>
<% end %>
I really for can't work out at all what is causing this undefined method error so any help people can offer would really be much appreciated! Thanks in Advance :)
The way you have hooked things up, you don't need :url => contact_path
<%= form_for #message do |f| %>
will work just fine.
I have a form wizard setup with 4 steps that all work on the same model.
When I fill out the first one and click submit (and am redirected to step 2) the form is already filled out and a duplicate, identical form added below. This happens for each step.
How can i render a completely new form after i click submit on step 1 and am taken to 2 (or 3/4)?
step 1
<%= simple_form_for #user, url: wizard_path do |f| %>
<h2 class="signup">Step 3: Friends Birthday</h2>
<%= f.simple_fields_for :events do |event_f| %>
<%= event_f.input :name, :placeholder => 'Enter Persons Name' %>
<%= event_f.input :date, :as => :date_picker, :input_html => { :class => 'special' } %>
<%= event_f.input :reccuring, :label => false, :inline_label => 'Remind me of this event every year?' %>
<h3>Choose up to 3 interests for this person</h3>
<%= event_f.association :interests, :label => false, :as => :check_boxes %>
<%= event_f.input :kind, :as => :hidden, :input_html => { :value => "Birthday" } %>
<%end%>
<%= link_to "skip this step", next_wizard_path %>
<%= f.button :submit, 'Submit' %>
<%end%>
step 2
<%= simple_form_for #user, url: wizard_path do |f| %>
<h2 class="signup"> Married? Add your anniverarsy</h2>
<%= f.simple_fields_for :events do |anniversary_f| %>
<%= anniversary_f.input :name %>
<%= anniversary_f.input :date, :as => :date_picker, :input_html => { :class => 'special' } %>
<h3>Choose up to 3 interests for this person</h3>
<%= anniversary_f.association :interests, :as => :check_boxes, :label => false %></li>
<%= anniversary_f.input :kind, :as => :hidden, :input_html => { :value => "Anniversary" } %>
<%end%>
<%= link_to "skip this step", next_wizard_path %>
<%= f.button :submit, 'Submit' %>
<%end%>
Controllers
events_controller.rb
def new
#user = current_user
#event = Event.new
end
def create
#user = current_user
#event = Event.new(params[:event])
#event.user = User.find(current_user.id)
end
users_controller
class UsersController < ApplicationController
before_filter :authenticate_user!
def create
#user = User.new(params[:user])
if #user.save
redirect_to user_steps_path
else
render :new
end
end
When you complete one step then intilize new object and bind this second form to new object.Repeat this step for 3rd and 4th form.
Mean every time initilize
#user = User.new
Bassicaly my problem what to do if i have 3 forms and one submit button.
I want to create a form which sends email to each recipient and then create new record in free_registration_coupons table.
I need validation of email for this form.
Model FreeRegistrationCoupon: recipient_email, token, sender_id
For now i have this:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = [params[:recipient_email_1], params[:recipient_email_2], params[:recipient_email_3]]
emails.reject!{ |e| e.eql?("") }
if emails.present?
emails.each do |e|
FreeRegistrationCoupon.create(:recipient_email => e, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
redirect_to(:back)
end
end
end
class FreeRegistrationCoupon < ActiveRecord::Base
before_save :generate_token
attr_accessor :recipient_email, :sender_id
validates :recipient_email, :presence => true, :email => true
def generate_token
self.token = SecureRandom.hex
end
end
This is form which is in other controller CarsController#confirm:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<!-- errors -->
<%= label_tag :recipient_email_1 %>
<%= text_field_tag :recipient_email_1 %>
<%= label_tag :recipient_email_2 %>
<%= text_field_tag :recipient_email_2 %>
<%= label_tag :recipient_email_3 %>
<%= text_field_tag :recipient_email_3 %>
<%= submit_tag %>
<% end %>
I think you should have defined your form using:
<%= form_tag :controller => 'free_registration_coupons', :action => "send_invitations" do %>
<%= #error_message %>
<%= label_tag "recipient_email[1]" %>
<%= text_field_tag "recipient_email[1]" %>
<%= label_tag "recipient_email[2]" %>
<%= text_field_tag "recipient_email[2]" %>
<%= label_tag "recipient_email[3]" %>
<%= text_field_tag "recipient_email[3]" %>
<%= submit_tag %>
<% end %>
This way it will be easier to treat all email address on your controller and you can track those errors to display them afterwards:
class FreeRegistrationCouponsController < ApplicationController
def send_invitations
emails = params[:recipient_email]
emails.reject!{ |param, value| value.eql?("") }
errors = []
if emails.any?
emails.each do |param, value|
validation_result = FreeRegistrationCoupon.save(:recipient_email => value, :sender_id => current_user.id)
#MAILER
end
redirect_to root_path, :notice => "You just send #{emails.size} invitations!"
else
#error_message = "You have to include, at least, one e-mail address!"
render :name_of_the_action_that_called_send_invitations
end
end
end
I didnt test this code. Hope it helps!
Ok so I am having a problem with doing multiple forms in rails. here is the code below
Models
class Profile < ActiveRecord::Base
belongs_to :user
has_attached_file :avatar, :styles => { :medium => "134x137>", :thumb => "111x111>", :tiny => "32x38>" }
validates_attachment_content_type :avatar, :content_type => ['image/pjpeg','image/jpeg', 'image/x-png', 'image/png', 'image/gif']
class User < ActiveRecord::Base
has_one :profile, :dependent => :destroy
Profile Controller
def edit
#user = User.find(params[:id])
#profile = #user.profile
end
Profiles Edit View
<% form_for #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<input id="send_update" name="send" type="submit" value="Update" />
<% end %>
<% form_for #profile , :html => { :multipart => true } do |f| %>
<%= render :partial => 'form', :locals => {:f => f, :profile => #profile, :title => 'Edit Profile'} %>
<%= submit_tag 'Update', :style => 'text_align:right'%>
<% end %>
Profile _form partial
<label>Upload Avatar</label>
<tr><%= f.file_field :avatar %></tr>
So basically I have two forms in the edit view and when i click on the second Update to update the avatar I go to the users update and i get this flash error "Sorry, something went wrong"
def update
#user = User.find(params[:id])
current_email = #user.email
if #user.update_attributes(params[:user])
UserMailer.deliver_email_changed (#user) if email_changed?(current_email, #user.email)
flash[:notice] = "<h1>Account updated!</h1>"
redirect_to edit_user_path(#user)
else
flash.now[:error] = "Sorry, something went wrong"
render :action => :edit
end
end
My questions are this
Is there a better way to structure this so maybe i have one form?
Why is it not saving now and whats causing the issue?
The method you are using to update is wrong, it is syntactically invalid (you're missing an end). It should be:
def update
#user = User.find(params[:id])
current_email = #user.email
if #user.update_attributes(params[:user])
UserMailer.deliver_email_changed (#user) if email_changed?(current_email, #user.email)
flash[:notice] = "<h1>Account updated!</h1>"
redirect_to edit_user_path(#user)
else
flash.now[:error] = "Sorry, something went wrong"
render :action => :edit
end
end
It should be two forms indeed, as I'm guessing you don't want any of the values of one form to submit if the user performs the other action.
Now, organize your controllers. You are calling #user = User.find(params[:id]) on your ProfilesController, but the id you're passing is an user's one. Either this should be on the user's controller, and update the associated profile from there, or you should receive the id of the profile object instead.
I'd go with the first one. You can update the Profile object of a user using accepts_nested_attributes_for, and your forms would be like:
<% form_for #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.text_field :email %>
<%= f.password_field :password %>
<%= f.password_field :password_confirmation %>
<%= f.submit, :id => ... %>
<% end %>
<% form_for #user, :html => { :multipart => true } do |f| %>
<% f.fields_for :profile do |profile_form| %>
<%= render :partial => 'form', :locals => {:f => profile_form, :title => 'Edit Profile'} %>
<%= submit_tag 'Update', :style => 'text_align:right'%>
<% end %>
<% end %
If the error is that the password cannot be blank, may be due to a validates_presence_of :password, :password_confirmation. You should use a conditional validation there
Troubleshooting would help:
Remove the if/else and keep the #user.update_attributes(params[:user]). Rails would give you a more detailed error message.
Check the form structure (html source) especially for field naming.
Check the log files for DB statements
HTH