Rails - I want to allow a user to change only their profile
First, I think you might be missing a =
sign.
<% if current_user.contact == @contact %>
<%= render 'form' %>
<% end %>
Second, on contacts#edit
action do something like
if current_user.contact != @contact
flash[:error] = "you've been naughty"
# also its recommended to record such an abuse attempt by logging or something
else
# do rest here
end
You can also do more with something like cancan or another authorization gem (for instance, if you want to allow only specific users to edit a contact and not only current_user
).
Can only create one user profile - Rails 4.2.4
We use this setup with some of our apps - User
-> Profile
.
In short, you should build the profile at User creation. Then you can edit the profile as you need. Your problem of having a Profile.new
method is very inefficient...
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
before_create :build_profile #-> saves blank associated "Profile" object after user create
end
This will mean that each time a User
is created, their corresponding Profile
object is also appended to the db.
This will give you the capacity to edit the profile as required:
#config/routes.rb
resources :users, path_names: { edit: "profile", update: "profile" }, only: [:show, :edit, :update]
This will give you the opportunity to use the following:
#app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :authenticate_user!, only: [:edit, :update]
before_action :authorize, only: [:edit, :update]
def show
@user = User.find params[:id]
end
def edit
@user = current_user
end
def update
@user = current_user
@user.update user_params
end
private
def authorize
id = params[:id]
redirect_to user_show_path(id) if current_user.id != id #-> authorization
end
def user_params
params.require(:user).permit(:x, :y, :z, profile_attributes: [:homepage, :other, :profile, :attributes])
end
end
The view/form would be the following:
#app/views/users/edit.html.erb
<%= form_for @user do |f| %>
<%= f.fields_for :profile do |f| %>
<%= f.text_field :homepage %>
...
<% end %>
<%= f.submit %>
<% end %>
In regards your current setup:
def new
@profile = current_user.profile.new
end
def create
@profile = current_user.profile.new profile_params
if @profile.save
redirect_to user_path(params[:id]), notice: "Profile Created!"
else
render action: :new
end
end
private
def profile_params
params.require(:profile).permit(:x, :y, :z)
end
How to allow ONLY Admins to create new users?
You could achieve this numerous ways. What I have done in the past is a combination of showing\hiding the links in the views and checking the user in the controller. I've assumed you have a form with the user details that you will submit to the user controller.
I've included a controller from one app I worked on below.
The first thing I do is to check to see if the user is authenticated (we use google for this but if you have set up devise you won't need this and probably have your own authentication in place). Devise will have created the current_user object if you have logged in which should include your "role" attribute. In the standard user create you can check the current user.role and simply redirect if the current_user.role is not 1 (I assumed 1 means admin).
class UsersController < ApplicationController
# Set the user record before each action
before_action :set_user, only: [:show, :edit, :update, :destroy]
# User must authenticate to use all actions in the users controller
before_filter :authenticate_user!
def create
if current_user.role = 1 then
@user = User.new(user_params)
@user.password = Devise.friendly_token[0,20]
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.json { render action: 'show', status: :created, location: @user }
else
format.html { render action: 'new' }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
else
format.html { redirect_to @user, notice: 'You do not have sufficient rights to set up a new user.' }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
@user = User.find(params[:id])
rescue ActiveRecord::RecordNotFound
flash[:notice] = "User record does not exist"
redirect_to users_url
end
end
building a has_one relation with devise user in rails
By far the best way to create a profile
for a user
is to build it when the User
object is created:
#app/models/user.rb
class User < ActiveRecord::Base
has_one :profile
before_create :build_profile
accepts_nested_attributes_for :profile
end
#app/models/profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
This will build a blank profile
each time a new user
is created. It means that each user will only ever have one profile, which they'll be able to populate & edit.
In regards your issue, there are several points:
- Don't list "profiles" with an index, list users & pull their profile data
- If managing a profile, nest it under the associative user model
Here's how to do that:
# config/routes.rb
resources :users, only: :index
resource :profile, only: [:show, :update]
#app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
def show
end
def update
redirect_to :show if current_user.update profile_params
end
private
def profile_params
params.require(:user).permit(profile_attributes: [:name])
end
end
#app/views/profiles/show.html.erb
<%= form_for current_user, url: profile_path do |f| %>
<%= f.fields_for :profile do |p| %>
<%= p.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
Update
My post above is exactly what we do.
The way it works is very simple -- when a User
is created (IE they have gone to the trouble of filling out their details), the Rails backend automatically creates a blank Profile
object.
This does several things:
Always makes sure you have a
Profile
for each user (you don't have to go to the bother of making them "create" a profile).Gives you the ability to validate only inputted data on a created
Profile
(not having to guess whether it's already been done).
--
If you're getting undefined method build_profile
, it means your associations are incorrect.
All singular associations have build_[association]
as defined instance methods. The code I provided fires build_profile
for a has_one
association. The only time it would be "undefined" would be if the association was plural
--
Update
This suggests a routing error.
Considering it appears at root
, I think the problem is here:
#app/views/layouts/application.html.erb
<%= link_to "Profile", profile_path %>
You don't have edit_profile_path
-- it should just be profile_path
Ruby on Rails - Creating a profile when user is created
You should really do this as a callback in the user model:
User
after_create :build_profile
def build_profile
Profile.create(user: self) # Associations must be defined correctly for this syntax, avoids using ID's directly.
end
end
This will now always create a profile for a newly created user.
Your controller then gets simplified to:
def create
@user = User.new(user_params)
if @user.save
redirect_to root_url, :notice => "You have succesfully signed up!"
else
render "new"
end
end
Related Topics
How to Understand the #Dup and #Clone Operate on Objects Which Referencing Other Objects
Why Do Numeric String Comparisons Give Unexpected Results
Brew Install Coreutils Failing 10.9.5 Osx
Why Does Single '=' Work in 'If' Statement
Why Can't I Change the Value of Self
How to Find the Most Recently Modified Folder in a Directory Using Ruby
Solr or Sphinx? Which Is Better
Fresh Rails App Defaulting to Postgres Instead of SQLite3
Ruby on Windows7 - Could Not Find Gem
Gitlab: Invocation of Gitlab-Shell
Why Does Trying to Use Grape with Rails Fail with "Uninitialized Constant API"
How to Create an Association Between Two Rails Models
Actionmailer Smtp "Certificate Verify Failed"
Why Do We Need Nginx with Thin on Production Setup
How to Force To_Yaml to Output Long Strings in Literal Block Style