Rails update form throwing "undefined method `[]' for nil:NilClass" - ruby-on-rails

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

Related

Rails 6: Only one profile per user should be created

I'm currently working on a Rails 6 application. I have the following association. A User has a Profile and a Profile belongs to a User. When editing a profile for a user I ended up having two profiles for the user. I would like to have only one profile per user.
Edit form: profile/edit.html.erb
<%= form_for #profile do |f| %>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar, as: :file, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :full_name, 'Full Name' %>
<%= f.text_field :full_name, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :city, 'City' %>
<%= f.text_field :city, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :bio, 'Bio'%>
<p> Why did you join ArtsySpace?
What should other people here know about you?
</p>
<%= f.text_field :bio, class: "form-control"%>
</div>
<div class="form-group">
<%= f.submit "Edit profile", class: "btn btn-primary" %>
</div>
<% end %>
I see from the console that user 1 has 2 profiles. I'm not sure how a profile was created maybe I hit the create method from profile controller but mistake but I would like for this not to happen. Is there a validation for only one profile to belong to user?
class ProfilesController < ApplicationController
def new
#profile = current_user.build_profile
end
def create
#profile = current_user.create_profile(profile_params)
#profile.avatar.attach(params[:profile][:avatar])
if #profile.save
redirect_to #post
else
render 'new'
end
end
def show
#profile = Profile.find(params[:id])
end
def edit
#profile = current_user.profile
end
def update
#profile = current_user.profile
if #profile.update!(profile_params)
redirect_to #profile, notice: 'Profile was successfully updated.'
else
render :edit
end
end
def delete
#profile = current_user.profile.find(params[:id])
#profile.destroy
end
private
def profile_params
params.require(:profile).permit(:full_name, :city, :bio, :avatar)
end
end
I'm not sure if the issue comes from the way the routes are configured?
Rails.application.routes.draw do
devise_for :users
devise_scope :users do
resources :profiles, only: [:edit, :update]
end
resources :profiles, only: [:show]
resources :posts do
resource :comments, only: %i[show new create edit update]
end
end
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 :posts
has_one :profile
accepts_nested_attributes_for :profile
end
From the below snippet you can see that a user has 2 profiles for user_id: 1
[#<Profile id: 3, user_id: 1, full_name: "steven ", city: "diego ", bio: "Because im ", created_at: "2019-06-12 23:11:49", updated_at: "2019-06-16 18:49:22">, #<Profile id: 4, user_id: 1, full_name: "andrew", city: "Tony", bio: "because i know ", created_at: "2019-06-12 23:12:35", updated_at: "2019-06-16 18:51:22">]
Not sure where the issue came from.
I once solved this by doing (I know it's strange, I'm just putting it out here as an idea)
has_one :profile
has_many :profiles
validates :profiles, length: { in: 0..1 }
https://guides.rubyonrails.org/active_record_validations.html#length
I decided to not to use a profile controller and just have one model User that can be used as the profile.

Issue creating a User through an Account model Rails 5 with Devise

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.

Viewing Serialized Hash Rails

I am trying to build a default checklist for my application by serializing a hash. I don't know how to pull the information in my setup_checklist hash to my views. Any help is appreciated.
Here is my user model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
serialize :checklist, Hash
before_create :setup_checklist
private
def setup_checklist
self.checklist = {
"Organize Your Finances" => false,
"Approval Letter" => false,
"Get a Real Estate Agent and look for houses" => false,
"Find lawyer" => false,
"Get the mortgage" => false,
"Apprisal and inspection" => false,
"Close the deal" => false
}
end
end
my view.html.erb
<%= form_for :checklist do |f| %>
<%= f.check_box :checklist %>
<% end %>
I know I am not even close but a steer in the right direction would be great
To display checkboxes from a hash, you need to iterate the hash and create a checkbox for each element; for example:
<%= form_for #user do |f| %>
<% #user.checklist.each do |key, value| %>
<%= f.check_box key %>
<% end %>
<% end %>
May not be the prettiest, but this seems to be working for me
view.html.erb
<%= form_for #user do |f| %>
<%= f.fields_for :checklist do |c| %>
<% #user.checklist.each do |todo, completed| %>
<%= c.check_box todo %> <%= c.label todo %><br />
<% end %>
<% end %>
<%= f.submit %>
<% end %>
and then in the controller
def update
#user = User.find(params[:id])
#user.checklist.each do |todo, completed|
#user.checklist[todo] = params[:user][:checklist][todo] == "1"
end
#user.save
redirect_to action: :show, id: params[:id]
end
you could, of course, move that controller code into something like an update_checklist method on your model or refactor however works for you, but this should get you started.
If you use strong params, you'll need to permit each possible checklist value and it'll set them to '0' or '1' instead of true or false, but the controller could be
#user.update(params.required(:user).permit(checklist: ["Organize Your Finances", ...]))

Undefined method, avatar and name for nil:NilClass

undefined method avatar?' for nil:NilClass
undefined methodname' for nil:NilClass
Hi, I'm receiving the following errors in my partial. The reason I listed both is because after commenting out the line causing the first error message, I get the second error which leads me to believe the problem isn't with "avatar" or "name" specifically, but with something else,though I don't know what. In rails console, I'm able to call user and name on a comment. I also seeded the database using Faker if that matters. Here's the partial.
<%= content_tag :div, class: 'media', id: "comment-#{comment.id}" do %>
<%= link_to '#', class: 'pull-left' do %>
<%= image_tag(comment.user.avatar.small.url) if comment.user.avatar? %>
<% end %>
<div class="media-body">
<small>
<%= comment.user.name %> commented <%= time_ago_in_words(comment.created_at) %> ago
<% if policy(comment).destroy? %>
| <%= link_to "Delete", [#topic, #post, comment], method: :delete %>
<% end %>
</small>
<p><%= comment.body %></p>
</div>
<% end %>
Also, please see the render.
<div class="col-md-4">
<% if policy(Comment.new).create? %>
<h4>Leave a comment</h4>
<br/>
<%= render partial: 'comments/comment', locals: { topic: #topic, post: #post, comment: #comment } %>
<% end %>
</div>
The below are my user model and comments_controller
class UsersController < ApplicationController
before_filter :authenticate_user!
def update
if current_user.update_attributes(user_params)
flash[:notice] = "User information updated"
redirect_to edit_user_registration_path(current_user)
else
render "devise/registrations/edit"
end
end
private
def user_params
params.require(:user).permit(:name, :avatar)
end
end
Comments_controller
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.find(params[:post_id])
#comments = #post.comments
#comment = current_user.comments.build(comment_params)
#comment.post = #post
#new_comment = Comment.new
authorize #comment
if #comment.save
redirect_to [#topic, #post], notice: "Comment was submitted successfully."
else
flash[:error] = "There was an error submitting the comment. Please try again."
end
end
I've already reset the database, but to no avail. Stuck as to what the issue is. Thanks for your help.
Please see below for my User and Comment models.
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
default_scope { order('created_at DESC') }
validates :body, length: { minimum: 5 }, presence: true
after_create :send_favorite_emails
private
def send_favorite_emails
self.post.favorites.each do |favorite|
if favorite.user_id != self.user_id && favorite.user.email_favorites?
FavoriteMailer.new_comment(favorite.user, self.post, self).deliver
end
end
end
end
User model
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
has_many :comments
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
mount_uploader :avatar, AvatarUploader
def role?(base_role)
role == base_role.to_s
end
def favorited(post)
self.favorites.where(post_id: post.id).first
end
def voted(post)
self.votes.where(post_id: post.id).first
end
private
end
If you're getting
undefined method foo for nil:NilClass
it's that the thing you're calling your method on is nil.
So in your case, you're calling avatar? and name on something nil.
Looking at your code, it's clear comment.user is (a) what those methods are called on, and hence (b) what is nil.
Result: your comment has no user. Either enforce all comments (including new/empty/stub ones) to have an user (blank user?), or make your view so that a user is not necessary.
The issue was discovered. In the partial render
comment: #comment
should be
comment: comment

rails couldnt find profile

I'm currently getting this error. Couldn't find Profile without an ID. I am trying to create a profile form that can be edited and updated once a user has signed up. Ive setup a has_one association of users to profiles. Heres my controller for profile.
Note - Using devise
routes.rb
resource :profile , :only => [ :edit, :update]
profile_controller.rb
class ProfilesController < ApplicationController
def edit
#profile = Profile.find(params[:id])
end
def update
#profile = Profile.find(params[:id])
#profile.update(params[:profile].permit(:example,:example))
end
end
user.rb model - create profile when new user signs-up
class User < ActiveRecord::Base
has_one :profile
after_create :create_profile
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
private
def create_profile
self.profile = Profile.create
end
end
Heres the form - edit.html.erb
<div class="container">
<div class="row">
<div class="col-lg-3">
<%= render 'layouts/sidenav' %>
</div>
<%= form_for :profile, method: :patch do |f| %>
<div>
<%= f.label :example %>
<%= f.text_field :example %>
</div>
<div>
<%= f.label :example %>
<%= f.text_field :example%>
</div>
<div>
<%= f.submit %>
</div
<% end %>
</div>
</div>
Your form_for is using a symbol for profile rather than the instance variable...
You need:
<%= form_for #profile do |f| %>
# etc
<% end %>
Also, you shouldn't need the 'patch' method in there as Rails knows what to do with an ActiveRecord instance in a form_for.
Edit:
If you're trying to use a path with doesn't include the profile id to edit you'll have to do something like so in your edit action (assuming you have current_user to refer to the user who is logged in):
def edit
#profile = current_user.profile
end
When you use the params[:id] to find the profile, Rails is looking for an id in the url params (which you don't have).
Likewise in your update action you'll need to find the profile based on the user.
def update
#profile = current_user.profile
# etc
end
Couldn't find Profile without an ID would be a typical exception when
Profile.find(params[:id])
is passed a null value. So it seems params[:id] is not being set.

Resources