What I'm trying to accomplish:
When a user registers with my app they are taken to a new account creation page. This is where they enter their desired subdomain. from this form I also want to create the owner (a user class).
The problem:
As it sits right now, when i fill out the generated form (below)
<%= form_for #account do |f| %>
<%= fields_for :owner do |o| %>
<p>
<%= o.label :f_name %>
<%= o.text_field :f_name %>
</p>
<p>
<%= o.label :m_name %>
<%= o.text_field :m_name %>
</p>
<p>
<%= o.label :l_name %>
<%= o.text_field :l_name %>
</p>
<p>
<%= o.label :email %>
<%= o.email_field :email %>
</p>
<p>
<%= o.label :password %>
<%= o.password_field :password %>
</p>
<p>
<%= o.label :password_confirmation %>
<%= o.password_field :password_confirmation %>
</p>
<% end %>
<p>
<%= f.label :subdomain %>
<%= f.text_field :subdomain %>
</p>
<%= f.submit %>
<% end %>
and try to submit the form, I get the following rails server output:
Started POST "/accounts" for 127.0.0.1 at 2018-04-08 21:52:57 -0600
Processing by AccountsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4yUhk6N40udNBMoBJz/sFzbjC/RUtU7FVyHe9NlhtBkmpGEMZE0+xMcD7E6GLOjgp02hbkrbuMNLQ5gBjh+kvA==", "owner"=>{"f_name"=>"xxxxx", "m_name"=>"xxxxx", "l_name"=>"xxxxx", "email"=>"xxxxx#xxxxxnltd.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "account"=>{"subdomain"=>"testinga"}, "commit"=>"Create Account"}
(0.2ms) BEGIN
Account Exists (0.6ms) SELECT 1 AS one FROM "accounts" WHERE LOWER("accounts"."subdomain") = LOWER($1) LIMIT $2 [["subdomain", "testinga"], ["LIMIT", 1]]
(0.1ms) ROLLBACK
Rendering accounts/new.html.erb within layouts/application
Rendered accounts/new.html.erb within layouts/application (2.4ms)
Completed 200 OK in 49ms (Views: 21.5ms | ActiveRecord: 8.3ms)
Now when I read the output I cant seem to find why this is rolling back and not saving. I do see it telling me an account already exists whit that subdomain, however this is a CLEAN database and there are no accounts saved in it! When I run byebug just before the #account.save in the accounts controller (below) there are no error messages or details I can find.
My AccountController: (I've left the byebug in the controller, perhaps im putting it in the wrong place?)
class AccountsController < ApplicationController
def index
end
def show
end
def new
#account = Account.new
#account.build_owner
end
def create
#account = Account.new(account_params)
byebug
if #account.save
redirect_to root_path, notice: 'Account creates successfully.'
else
render action: 'new'
end
end
def edit
end
def update
end
def destroy
end
private
def account_params
params.require(:account).permit(:subdomain, :owner_id, :plan_id, :account_verified, :account_status, owner_attributes: [:id, :email, :password, :password_confirmation, :f_name, :m_name, :l_name, :office_country_code, :office_phone_number, :mobile_country_code, :mobile_phone_number])
end
end
My Account model
class Account < ApplicationRecord
RESTRICTED_SUBDOMAINS = %w(www admin loadlead)
belongs_to :owner, class_name: 'User'
has_many :users
validates :owner, presence: true
validates :subdomain, presence: true,
#uniqueness: { case_sensitive: false },
format: { with: /\A[\w\-]+\Z/i, message: 'contains invalid characters'},
exclusion: { in: RESTRICTED_SUBDOMAINS, message: 'restricted name'}
before_validation :downcase_subdomain
accepts_nested_attributes_for :owner
protected
def downcase_subdomain
self.subdomain = subdomain.try(:downcase)
end
end
My User model
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable
belongs_to :account
end
Any assistance here would be greatly appreciated! I have no idea where I'm going wrong with this? Thanks in advance.
Try to call fields_for on f builder instead:
<%= form_for #account do |f| %>
<%= f.fields_for :owner do |o| %>
<p>
<%= o.label :f_name %>
<%= o.text_field :f_name %>
</p>
# ....
<% end %>
# ....
<%= f.submit %>
<% end %>
And you can remove :owner_id, this attribute value will be set automatically by Rails when we're using :accepts_nested_attributes_for.
You are calling #account.save which does not raise an exception. It returns true if everything is fine, or returns false when the validation fails (if #account.valid? returns false).
If there are any validation errors, you can check them by calling:
pry(main)> #account.valid?
pry(main)> false
pry(main)> #account.errors
That should help you debug the issue.
Related
Could you help me? I got an error Couldn't find User without an ID, I was thinking about make like a blog service, I wanted to implement nest attribute without accepts_nested_attributes_for, so I've been using
form object, but I couldn't send form object user's parameter,
controller
class BlogsController < ApplicationController
before_action :authenticate_user!
def index
#current = current_user
end
def new
#blogs = BlogForm.new
end
def create
#blogs = BlogForm.new(blog_params)
if #blogs.save
redirect_to user_blogs_path
else
end
end
def edit
end
def show
end
private
def blog_params
params.require(:blog_form).permit(:title , :content , :user_id)
end
end
form html
<%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
<% f.label :title %>
<%= f.text_field :title %>
<% f.label :content %>
<%= f.text_area :content %>
<% f.label :user_id %>
<% f.hidden_field :user_id , value: current_user.id%>
<%= f.submit "create", class: "btn btn-primary" %>
<% end %>
blog_form
class BlogForm
include ActiveModel::Model
attr_accessor :title, :content, :user_id
def to_model
#user = User.find(user_id)
#blogs = #user.blogs.new(title: title , content: content , user_id: user_id)
end
def save
return false if invalid
to_model.save
end
end
blogs.rb
class Blog < ApplicationRecord
belongs_to :user
validates :title ,presence: true
validates :content ,presence: true
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :blogs
def email_required?
false
end
def email_changed?
false
end
def will_save_change_to_email?
false
end
end
log
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
Started GET "/users/1/blogs/new" for 127.0.0.1 at 2020-01-19 16:29:03 +0900
Processing by BlogsController#new as HTML
Parameters: {"user_id"=>"1"}
Rendering blogs/new.html.erb within layouts/application
Rendered blogs/shared/_form.html.erb (Duration: 3.0ms | Allocations: 1143)
Rendered blogs/new.html.erb within layouts/application (Duration: 10.5ms | Allocations: 1228)
Completed 500 Internal Server Error in 16ms (ActiveRecord: 0.0ms | Allocations: 1715)
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path,local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
after, I tried coreyward's way, but I couldn't,
rocessing by BlogsController#new as HTML
Parameters: {"user_id"=>"1"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 1], ["LIMIT", 1]]
Rendering blogs/new.html.erb within layouts/application
Rendered blogs/shared/_form.html.erb (Duration: 6.9ms | Allocations: 1082)
Rendered blogs/new.html.erb within layouts/application (Duration: 9.4ms | Allocations: 1166)
Completed 500 Internal Server Error in 114ms (ActiveRecord: 2.3ms | Allocations: 11134)
ActionView::Template::Error (Couldn't find User without an ID):
1: <%= form_with model: #blogs , url: user_blogs_path(#user),local: true do |f| %>
2:
3: <% f.label :title %>
4: <%= f.text_field :title %>
app/forms/blog_form.rb:6:in `to_model'
app/views/blogs/shared/_form.html.erb:1
app/views/blogs/new.html.erb:4
The route helper user_blogs_path probably expects an argument for the user. Something like this:
user_blogs_path(#user)
Which goes in this line:
<%= form_with model: #blogs , url: user_blogs_path(#user),local: true do |f| %>
This is just a really strange and awkward way of doing a nested resource. This really has very little to do with nested attributes which used when you need to create or update two (or more) associated records in the same request.
# routes.rb
resources :users do
resources :blogs,
only: [:new, :show, :create],
shallow: true
end
class BlogsController
before_action :set_user, only: [:new, :create]
# GET /blogs/1
def show
#blog = Blog.find(params[:id])
end
# GET /users/1/blogs/new
def new
#blogs = #user.blog.new
end
# POST /users/1/blogs
def create
#blogs = #user.blog.new(blog_params)
if #blog.save
redirect_to #blog
else
render :new
end
end
private
def set_user
#user = User.find(params[:user_id])
end
def blog_params
params.require(:blog).permit(:title, :content)
end
end
<%= form_with model: [#user, #blog], local: true do |f| %>
<% f.label :title %>
<%= f.text_field :title %>
<% f.label :content %>
<%= f.text_area :content %>
<%= f.submit "create", class: "btn btn-primary" %>
<% end %>
The purpose of the enroll model is to group three other models gymsite, user and role.
class Gymsite
has_many :users
class User
has_many :roles, through: :assigments
has_many :assigments
class Assigment
belongs_to :user
belongs_to :role
class Role
has many :assigments
has_many :users, through: :assigments
From the form at app/views/user/new.html.erb, input values are to be send to the app/controllers/user_controller.rb to perform the action 'enroll'. The problem is that all of the enroll_params are sent as is show in the browser, but at the controller, one of them become nil, in a recurrent way.
I have try by different means, including different form fields as select and check_boxes to try to solve this issue, with no result. I'm curios to learn what is the origin of the problem and how to solve it.Thanks
Browser processing information:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"0z2B2t7H5wYJjtJMn8zBIPJ7sJaFk706EPh5JVZsC76x GWeggrJi7RSxAu8GJJmsyrNP7NzVv6rNvmumNDyhsg==", "enroll"=> {"user_first_name"=>"Lorena", "user_last_name"=>"Cueva", "user_email"=>"lcueva#example.com", "user_password"=>"[FILTERED]", "user_password_confirmation"=>"[FILTERED]", "gymsite_id"=>"1", "u_rol"=>["", "2", "4"]}, "commit"=>"Enroll User"}
app/views/users/new.html.erb
<%= simple_form_for #enroll, url: users_path do |form| %>
<div class='row'>
<div class='columns medium-2'></div>
<div class='columns medium-4'>
<%= form.input :user_first_name %>
<%= form.input :user_last_name %>
<%= form.input :user_email %>
<%= form.input :user_password %>
<%= form.input :user_password_confirmation %>
</div>
<div class='columns medium-4'>
<%= form.input :u_rol, as: :check_boxes, collection: Role.all.pluck(:name, :id), input_html: {multiple: true}%>
<%= form.submit 'Enroll User'%>
<% end %>
</div>
</div>
<%= link_to 'Back', users_path %>
at app/controllers/users_controller.rb
def new
#enroll = Enroll.new
#roles = Role.all
end
def create
#enroll = Enroll.new(enroll_params)
if #enroll.save
redirect_to root_path
else
render :new
end
end
def enroll_params
params.require(:enroll).permit(:user_email, :user_password, :user_password_confirmation, :gymsite_id, :u_rol, :user_first_name, :user_last_name)
end
at the app/models/enroll.rb all the parameters perform correct, except u_rol that becomes nil.
class Enroll
include ActiveModel::Model
attr_accessor :user_email, :user_password, :user_password_confirmation, :gymsite_id, :u_rol, :user_first_name, :user_last_name, :user, :role
def save
if valid?
#find gymsite by gymsite_id
gym = Gymsite.find(gymsite_id)
#save user for a gymsite
#user = gym.users.create(first_name: user_first_name, last_name: user_last_name, email: user_email, password: user_password, password_confirmation: user_password_confirmation )
#assign roles to the user
u_rol.each do |n|
rol = Role.find(n)
#user.roles << rol
end
end
end
I solve this issue changing enroll_parms to
def enroll_params
params.require(:enroll).permit(:user_first_name, :user_last_name,:user_email, :user_password, :user_password_confirmation, :gymsite_id, u_rol: [])
end
in the app/views/users/new.html.erb, added 'include_hidden:
false' to get rid of the blank value:
<%= form.input :u_rol, as: :check_boxes, collection: Role.all.pluck(:name, :id), include_hidden: false, input_html: {multiple: true}%>
Now it works!. Thks #jvillian for the tip!
I'm trying to create an update form on Rails, for an object that has a foreignkey to another. However, it throws this error. I'm still very greenhorn with Ruby on Rails and have just been following a video tutorial, so I'm not quite sure how to interpret this. I am current using rails 5.0.0
In travelers_controllers.rb, below line
#prf = update_prof_params["profiles_attributes"]["0"]
throws the error
undefined method `[]' for nil:NilClass
edit.html.erb
<div class="col-md-7 col-md-offset-3 main">
<% provide(:title, "Edit user")%>
<center><h1>Update your profile</h1></center>
<%= form_for(#person) do |f| %>
<%= render 'shared/error_messages' %>
<div class="col-md-12">
<%= render 'layouts/profilefields', f: f %>
<%= f.submit "Save Changes", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
_profilefields.html.erb
<%= f.fields_for :profiles do |prf|%>
<!--
<% if !#profileInfo["avatar"].blank? %>
<%= image_tag #contactInfo.avatar_url(:medium).to_s, :class=>"profilePhoto" %>
<% end %>
<div class="photoPreview">
<i class="fa fa-upload photoUpload"></i>
<p id="uploadClick">Click to Upload</p>
</div>
<%= prf.file_field :avatar, accept: 'image/png,image/gif,image/jpeg, image/jpg', id: 'uploadAvatar' %>
<p class="deletePhoto">Delete</p>
-->
<%= prf.label :about %>
<%= prf.text_field :about, :class => "form-control" %>
<%= prf.label :why %>
<%= prf.text_field :why, :class => "form-control" %>
<%= prf.label :goals %>
<%= prf.text_field :goals, :class => "form-control" %>
<%= prf.hidden_field :traveler_id, value: current_traveler.id %>
<% end %>
travelers_controller.rb
class TravelersController < ApplicationController
def edit
#person = Traveler.find(params[:id])
#profileInfo = Profile.find_or_initialize_by(traveler_id: params[:id])
##profileInfo[:email] = current_traveler.email
#This builds the form
#person.build_profile(#profileInfo.attributes)
end
def show
end
def update
#prf = update_prof_params["profiles_attributes"]["0"]
#prof = Profile.find_or_create_by(traveler_id: current_traveler.id)
if #prof.update_attributes(prf)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
private
def update_prof_params
params.require(:traveler).permit(profiles_attributes: [:about, :why, :goals,
:traveler_id])
end
end
and the models
class Profile < ApplicationRecord
belongs_to :traveler, foreign_key: "traveler_id"
end
class Traveler < ApplicationRecord
# Include default devise modules. Others available are:
# , :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
end
In TravelersController, the method update should be used for update traveler, not profile, so you can use mass-update via nested attribute like this:
def update
#traveler = Traveler.find(params[:id])
if #traveler.update(update_prof_params)
flash[:success] = "Profile updated"
redirect_to feed_path
else # Failed. Re-render the page as unsucessful
render :edit
end
end
So the above allow you to create/update profile which belongs to traveler. Besides, ensure the nested attribute was defined in your model:
traveler.rb
class Traveler < ActiveRecord::Base
# Your code here
#....
# Make sure define this
accepts_nested_attributes_for :profile
end
Update: The permitted params should be:
def update_prof_params
params.require(:traveler).permit(profile_attributes: [:about, :why, :goals, :traveler_id])
end
As you see profile_attributes should be used instead of profiles_attributes because traveler has one profile only
I'm implementing an invitation system and I want the new user form to pre-populate the user's email address in the email address field on the form (eventually, I will refactor this so it's not a form_field), so that the user doesn't have to type in all their information, just enter a password.
I have created the getter/setter methods in the users.rb model like this:
def invitation_token
invitation.invitation_token if invitation
end
def invitation_token=(invitation_token)
self.invitation = Invitation.find_by_invitation_token(invitation_token)
end
INVITATION MODEL
class Invitation < ActiveRecord::Base
#--== ASSOCIATIONS
belongs_to :sender, :class_name => 'User'
has_one :recipient, :class_name => 'User'
#--== CALLBACKS
before_create :generate_token
before_create :recipient_is_not_registered
before_create :decrement_sender_count, :if => :sender
#--== VALIDATIONS
validates_presence_of :recipient_email
#validate :recipient_is_not_registered
validate :sender_has_invitations, :if => :sender
#--== METHODS
private
def recipient_is_not_registered
if User.find_by_email(recipient_email)
false
else
true
end
end
def sender_has_invitations
unless sender.invitation_limit > 0
redirect_to root_url
end
end
def generate_token #TODO: MOVE to lib/generate_token.rb
self.invitation_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
def decrement_sender_count
sender.decrement! :invitation_limit
end
end
USER CONTROLLER
class UsersController < ApplicationController
def new
#user = User.new(:invitation_token => params[:invitation_token])
#user.email = #user.invitation.recipient_email if #user.invitation
end
def create
#user = User.new(user_params)
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thank you for signing up!"
else
render "new"
end
end
...
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :admin)
end
end
views/users/_form.html.erb
<%= form_for #user do |f| %>
<%= f.hidden_field :invitation_token %>
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.check_box :admin %>
<%= f.label :admin %>
</div>
<div class="actions"><%= f.submit %></div>
<% end %>
I was following Ryan Bates' RC#124 - Beta Invitations, and got stuck here. His code doesn't produce the error, so I should mention that this is a Rails 3.2.18 app.
When I reload the form, the user's email isn't populated in the form. The relevant log shows:
Started GET "/signup.914823d28d07b747213ec3de47f89ad537169e34" for 127.0.0.1
at 2016-04-30 20:24:47 -0600
Processing by UsersController#new as
User Load (1.0ms) SELECT "users".* FROM "users" WHERE "users"."auth_token" = 'rOHiKmDcceytxi_t151YIQ' LIMIT 1
Invitation Load (0.0ms) SELECT "invitations".* FROM "invitations" WHERE "invitations"."invitation_token" IS NULL LIMIT 1
Rendered users/_form.html.erb (5.0ms)
Rendered users/new.html.erb within layouts/application (6.0ms)
Completed 200 OK in 102.0ms (Views: 25.0ms | ActiveRecord: 3.0ms)
So it appears that the invitation_token isn't being passed in, since the log shows it is NULL.
I have gone over the RC code from top to bottom and can't find out why it's not being passed.
Any help would be appreciated. Thanks.
UPDATE: The output from the view source is:
<input id="user_invitation_token" name="user[invitation_token]" type="hidden" />, so it's not being passed along.
Set the value on the hidden field by passing the value: key:
<%= f.hidden_field :invitation_token, value: some_value %>
I am using Devise in my rails 4 app. I have a member model (for devise) and a user model that contains all the profile info for each member. A member accepts nested attributes for user. See the form below for a new registration as well as the custom routes in my routes.rb file.
My problem is this - everything works fine except validation of the user attributes. If I leave first name and last name blank, then it validates (but crashes like this:
But all the other validations for Member (the devise model) work - they end up on the /members page with the error message displayed as it should. I am unsure as to what is going on - shouldn't devise show the error messages for the invalid nested attributes?
class Members::RegistrationsController < Devise::RegistrationsController
# GET /resource/sign_up
def new
build_resource({})
self.resource.user = User.new
respond_with self.resource
end
# POST /resource
def create
super
resource.user.ip_address = request.remote_ip
resource.user.email = resource.email
resource.user.save!
end
private
def sign_up_params
allow = [:provider, :uid, :email, :password, :password_confirmation, user_attributes: [:member_id, :email, :first_name, :last_name, :institution, :city, :country, :job_title, :about, :tag_list, :picture, :ip_address]]
params.require(resource_name).permit(allow)
end
end
Member.rb
class Member < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :trackable
has_one :user, dependent: :destroy, autosave: true
accepts_nested_attributes_for :user
end
User.rb
class User < ActiveRecord::Base
belongs_to :member
validates :first_name, presence: true
validates :last_name, presence: true
end
In my routes.rb
devise_for :members, controllers: {registrations: 'members/registrations',
omniauth_callbacks: 'members/omniauth_callbacks',
sessions: 'members/sessions' }
In my members/registrations/new.html.erb
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), html: { role: "form" }) do |f| %>
<%= f.fields_for(:user) do |user_fields| %>
<%= user_fields.text_field :first_name, autofocus: true, class: 'form-control', :placeholder => "First name" %>
<%= user_fields.text_field :last_name, class: 'form-control', :placeholder => "Last name" %>
<% end %>
<%= f.email_field :email, autofocus: true, class: "form-control", :placeholder => "Email" %>
<%= f.password_field :password, class: "form-control", :placeholder => "Password" %>
<%= f.submit t('.sign_up', :default => "Sign up"), class: "btn btn-danger" %>
<% end %>
Here is the log for this
Started POST "/members" for ::1 at 2015-07-16 15:18:54 +0100
Processing by Members::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"sIxK1PTvrxNHUBdDXAtKFLLG27FvqypQ3mUvo398tHOMomi1S3I2o3vBM GwrF6PVN1NFBR5vnr8ezKP6XnMzqw==", "member"=>{"user_attributes"=>{"first_name"=>"", "last_name"=>""}, "email"=>"slfwalsh#gmail.com", "password"=>"[FILTERED]"}, "commit"=>"Sign up"}
(0.2ms) begin transaction
Member Exists (0.3ms) SELECT 1 AS one FROM "members" WHERE "members"."email" = 'slfwalsh#gmail.com' LIMIT 1
(0.1ms) rollback transaction
Rendered devise/shared/_links.erb (9.9ms)
Rendered members/registrations/new.html.erb within layouts/static_home_page (19.8ms)
Rendered layouts/_static_login_header.html.erb (4.1ms)
(0.1ms) begin transaction
(0.1ms) rollback transaction
Completed 422 Unprocessable Entity in 1499ms
The reason why you are seeing that page is because save! triggers validation errors if the validation for any of the attributes is failed and those errors are displayed in the page. On the other hand save doesn't display the validation errors but just cancels the transaction from being saved if any of the validations are failed. Changing your create method like below should solve your problem.
def create
super
resource.user.ip_address = request.remote_ip
resource.user.email = resource.email
resource.user.save
end