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.
Related
I have an admin namespaced route for a custom dashboard in my app. I have at least 8 models working, with error free crud operations--all except one. I am using the Devise Gem for user management and a User model. I have the User model in the admin namespace and the only operation I can get to work is changing role and destroy, but I can't create a new user from the dashboard. When I try to create a new user; I get the error "You are already signed in.".
controllers/admin/users_controller.rb
class Admin::UsersController < ApplicationController
before_action :authenticate_user!
before_action :admin_only, :except => :show
def index
#users = User.all
end
def show
#user = User.find(params[:id])
unless current_user.admin?
unless #user == current_user
redirect_to root_path, :alert => 'Access denied.'
end
end
end
def create
#user = User.new
end
def update
#user = User.find(params[:id])
if #user.update_attributes(secure_params)
redirect_to users_path, :notice => 'User updated.'
else
redirect_to admin_users_path, :alert => 'Unable to update user.'
end
end
def destroy
#user = User.find(params[:id])
user.destroy
redirect_to admin_users_path, :notice => 'User deleted.'
end
private
def admin_only
unless current_user.admin?
redirect_to root_path, :alert => 'Access denied.'
end
end
def secure_params
params.require(:user).permit!
end
end
models/user.rb
class User < ApplicationRecord
enum role: [:user, :admin]
def set_default_role
self.role ||= :user
end
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
views/admin/users/new.html.erb
<%= form_for User.new do |f| %>
<div class="form-group">
<%= f.label :first_name %><br/>
<%= f.text_field :first_name, autofocus: true %>
</div>
<div class="form-group">
<%= f.label :last_name %><br/>
<%= f.text_field :last_name %>
</div>
<div class="form-group">
<%= f.label :email %><br/>
<%= f.email_field :email %>
</div>
<div class="form-group">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br/>
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="from-group">
<%= f.label :password_confirmation %><br/>
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="form-group" style="margin-top: 1em">
<%= f.submit 'Sign up', class: 'btn btn--primary type--uppercase inner-link' %>
</div>
<% end %>
views/admin/users/new.html.erb (other method)
<%= form_for([:admin, #user]) do |f| %>
This is how all my name spaced forms are setup but this gives me the error First argument in form cannot contain nil or be empty
routes.rb
...
namespace :admin do
get '', to: 'dashboard#index', as: '/'
resources :pages
resources :articles
resources :categories
resources :tags
devise_for :users
resources :users
resources :albums
resources :banners
resources :products do
resources :variations
end
end
...
using the registration#create action is the appropriate architecture for this?
Because that action is built on a different concept, that the user is not logged in and there are a series of checks for that.
views/admin/users/new.html.erb
you are using registration#new controller action, which does not have the #admin valorized.
def new
#admin = Admin.find(params[:admin_id])
....
end
That is why you get the error, also this approach is not correct.
User will need to register with the traditional devise router, then you will also have to create a nested router for admins to create new users.
this is your link to the admin user registration#new
Your current view/controller#action has the #admin = Admin.find(params[:id]) valorized and the view has the following link
link_to new_admin_user_path(:admin, :user)
the new_admin_user_path is for url /admin/:admin_id/users/sign_up(.:format) that you define in your nested routes.
Step 2) decide which controller#action to use
Do you want to use the standard users/registrations#create or use a new action for this?
I believe you should enhance users/registrations#create by generating the controllers actions in your app as described in devise guide
then in devise controller registrations
def new
#admin = Admin.new(:params[:admin_id]) if params[:admin_id].present?
end
your registrations#new and #create will still trigger errors, you will have to read how devise creates this users by reading their amazing sourcecode and rdoc documentation, modify the process accordingly so that admins can create users by using that action otherwise the less DRY alternative is creating a new controller#action and using it to call an existing User method or a method you will create in the User model to create users. In the end it a User is just an entry in your users database table. Just creating in a similar fashion to other users. As the admin is creating the temporary password, encryption/security issues are not anymore that important. The User will have to change the password anyway.
So i have a User model which has a column called debts, what i want is the possibility of adding multiple debts to each user, something like an array of debts stored in the debts column, right now what is happening is that when i add a new debt to the user, it overrides the existing debt instead of adding it as an array object.
THIS IS MY CONTROLLER.
class UsersController < ApplicationController
def index
#students = User.with_role :student
#teachers = User.with_role :teacher
end
def show
#user = User.find(params[:id])
if request.patch?
#user.update(user_params)
end
end
private
def user_params
params.require(:user).permit(:debts)
end
end
THIS IS MY VIEW
<div class="container">
<div class="row">
<div class="col-sm-12">
<% if current_user %>
<% if current_user.username == #user.username %>
<h1><%= #user.username %>, logueado como usuario</h1>
<% elsif current_user.has_role? :admin%>
<h1><%= #user.username %>, logueado como admin</h1>
<%= form_for #user do |f| %>
<div class="form-group">
<%= f.text_field :debts, class: "form-control input-lg", placeholder: "Selecciona mes y año", id: "datepicker"%>
</div>
<div class="form-group">
<%= f.submit class: "btn btn-primary btn-lg btn-block" %>
</div>
<% end %>
<% end %>
<% end %>
</div>
</div>
</div>
THIS IS MY MODEL
class User < ActiveRecord::Base
rolify
after_create :assign_role
serialize :debts
def assign_role
if self.username == "admin"
self.add_role(:admin) if self.roles.blank?
elsif self.role == "student"
self.add_role(:student) if self.roles.blank?
elsif self.role == "teacher"
self.add_role(:teacher) if self.roles.blank?
end
end
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable
end
As a general approach to this, I would recommend to store the debts in a separate model which you can associate with the User model in a :has_many relationship.
But to answer the actual question, you could solve it by using a dedicated variable on the User model which holds the value that you want to add to the debts, and then manually add it to the array, for example:
class User < ActiveRecord::Base
# ... existing code
# Prepares an attribute that is not represented by a column in
# the database table, but can still be used in the forms
attr_accessor :add_debt
# Performs a callback on save to check if any debts should be added
# and if there is, it appends to the existing array instead of replacing
before_save do
if #add_debt.present?
self.debts << #add_debt
end
end
end
And in the views, you simple replace the attribute for the textfield like this:
<%= f.text_field :add_debt %>
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
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
I've searched everywhere for a solution but haven't come up with any.
The part that works: My app allows customers to create an account using a nested form. The data collected creates records in four models - accounts, users, accounts_users (because a user can be associated with many accounts), and profile (to store the user's fname, lname, phone, etc).
That part that doesn't work: Once logged in, I want the users to be able to add more users to their account using the form below. I don't receive any errors upon submit but I am brought back to the same form with no additional records created. Any help would be awesome!
Here is the nested form...
<%= form_for #user, :validate => true do |f| %>
<fieldset>
<%= f.fields_for :profile do |p| %>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<div class="field">
<%= p.label :phone %>
<%= p.text_field :phone %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Create New User', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
The ApplicationController scopes everything to the current_account like so:
def current_account
#current_account ||= Account.find_by_subdomain(request.subdomain) if request.subdomain
end
The UsersController
def new
#user = User.new
#user.build_profile()
#current_account.accounts_users.build() #Edit2: This line was removed
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
def create
#user = User.new(params[:user])
#user.accounts_users.build(:account_id => current_account.id) #Edit2: This line was added
if #user.save
# Send Email and show 'success' message
flash[:success] = 'An email has been sent to the user'
else
# Render form again
render 'new'
end
end
Models look like this:
class Account < ActiveRecord::Base
attr_accessible :name, :subdomain, :users_attributes
has_many :accounts_users
has_many :users, :through => :accounts_users
accepts_nested_attributes_for :users
end
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_many :accounts_users
has_many :accounts, :through => :accounts_users
has_one :profile
accepts_nested_attributes_for :profile
end
class AccountsUser < ActiveRecord::Base
belongs_to :account
belongs_to :user
end
class Profile < ActiveRecord::Base
belongs_to :user
attr_accessible :first_name, :last_name, :phone
end
Edit2: It turns out that I had required a password + password_comfirmation validation in the User model which prevented me from adding another user without these fields. I commented out these validations plus removed the line: current_account.accounts_users.build() in the 'new' action and added the line: #user.accounts_users.build(:account_id => current_account.id) in the 'create' action.
"I want the users to be able to add more users to their account using the form below." I assume you mean profiles (since your nested form is on profiles)?
If that's the case, I think your UsersController's create action isn't associating the profiles with users by using new.
Try this...
def new
#user = User.build
#profile = #user.profiles.build #build adds the profile to user's associated collection of profiles, but new doesn't
...
end
def create
#user = User.build(params[:user])
if #user.save
....
end
end
If you want the user to be associated with account, then you need to put the new and create actions in the AccountsController and do something similar to nest association of the users and profiles records.
Btw, the reason that it went back to new is because you render new at the end of the create, in case that's also part of the question. Hope that helps!