Rails 6: Only One Profile Per User Should Be Created

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:

  1. Don't list "profiles" with an index, list users & pull their profile data
  2. 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:

  1. 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).

  2. 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

Sample Image

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



Leave a reply



Submit