I recently started to learn ruby on rails and I was able to successfully create an app and add users with devise, also add an avatar to the user with paperclip.
Now I'm having a problem on how to display the avatar throughout the app. The avatar only displays in http:localhost:3000/users/... (within the devise folders) for exemple, but if I try to create a new page, model, controller http://localhost:3000/profile/ for exemple, using the the tag
<%= image_tag #user.avatar.url(:thumb) %>
the page will not load and will return this error
undefined method 'avatar?' for nil:NilClass
It's probably something really simple, but I can't figure out how to fix it.
My model user.rb looks like this:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_uniqueness_of :username
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }
attr_accessible :name, :username, :email, :password, :password_confirmation, :remember_me, :avatar
attr_accessor :current_password
end
And my controller looks like this:
class UserController < ApplicationController
def profile
end
end
Thanks!
On routes.rb, you should have something like this:
match "profile" => "user#profile"
On your UserController, you should have something like this:
class UserController < ApplicationController
def profile
#user = current_user
end
end
And then you'll be able to use #user.avatar.url. Also, pay attention that if you don't have a logged in user, current_user will be nil, and then you will have the error you described, so please add something like this on your controller:
class UserController < ApplicationController
before_filter :authenticate_user!
def profile
#user = current_user
end
end
And then, when a unauthenticated account tries to access /profile, it'll be redirected to the login form.
I am still new to Rails so correct me if I am wrong but I think this might work for you.
class UserController < ApplicationController
def profile
#user = User.find(current_user.username)
end
end
Related
First-time poster, many-time finder-of-answers on the site (thank you!). I'm using Rails 5.2.3, ruby-2.6.2 and Devise gem 4.6.2. I have not been able to get an answer to work, even though there are plenty somewhat related questions here, here, here and here.
When a new User signs up, I want them to select their Company from a dropdown list (already created) in the sign-up form. (Eventually, this will be an admin role, but that's beyond the scope of this question.)
I created a registrations controller and added code per a number of the previous posts. Update, I was not extending Devise as I should have as indicated here: Extending Devise Registration Controller. This is my new Registrations controller.
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
before_action :configure_account_update_params, only: [:update]
def new
#companies = Company.all
super
end
def create
#companies = Company.all
super
end
protected
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:company_id])
end
def configure_account_update_params
devise_parameter_sanitizer.permit(:account_update, keys: [:company_id])
end
end
And created new files in views/registrations with new.html.erb and edit.html.erb that I copied the exact code from the devise/registrations views.
I updated my routes.rb file to include:
devise_for :users, :controllers => { registrations: 'users/registrations', sessions: 'users/sessions' }
My User model is:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :company
accepts_nested_attributes_for :company
end
My Company model is:
class Company < ApplicationRecord
has_many :users
end
In the new user registration form, this works to provide the dropdown, but when I try to create the new user, it says: 1 error prohibited this user from being saved: Company must exist.
<%= f.collection_select :company, #companies, :id, :name, prompt: true %>
I thought this post would have the answer, but that appears to use Rails 3 and attr_accessible, which was deprecated in Rails 4.
I don't really understand what accept_nested_attributes_for :company does. The only thing in the Company model is the name.
Thank you in advance!
Welcome to StackOverflow.
In order to add more parameters to devise's sign up form, you'll need to sanitize the corresponding parameters using devise's sanitizer.
You should do that like this:
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:company_id])
end
end
You can find more information about parameter sanitizing and adding custom fields in this section of devise's readme
If you also want to add a select field including all the existing companies, you should add a collection select:
<%= f.collection_select :company_id, Company.all, :id, :name %>
Got it!
To extend the Devise controller, follow the help here: Extending Devise Registration Controller
The User models must also be updated to include the optional: true because here https://blog.bigbinary.com/2016/02/15/rails-5-makes-belong-to-association-required-by-default.html:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
belongs_to :company, optional: true
accepts_nested_attributes_for :company
end
I am using Devise to build a registration/authentication system into my application.
Having looked at quite a few resources for adding information to the devise model (e.g. username, biography, avatar URL, et cetera..) [resources include Jaco Pretorius' website, this (badly formed) SO question, and this SO question.
That's all fine and well -- it works. But my problem is that it's saving to the User model, which, according to database normalizations (also referencing this SO question), it should in fact be saving to a sub-model of User which is connected via has_one and belongs_to.
Thus far, I have created a User model via Devise. I have also created a UserProfile model via the rails generate script.
user.rb (for reference)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
end
user_profile.rb
class UserProfile < ActiveRecord::Base
belongs_to :user
end
timestamp_create_user_profiles.rb
class CreateUserProfiles < ActiveRecord::Migration
def change
create_table :user_profiles do |t|
t.string :username, null: false
t.string :biography, default: ""
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :user_profiles, [:user_id, :username]
end
end
My question, now, is, how does one collect the information for both of these models and ensure, via the devise registration form, that it all ends up in the right places?
I've seen resources about creating state machines (AASM, and the answer to this SO question. I've also seen information about creating a wizard with WICKED, and an article on the same topic.
These all seem too complicated for my use-case. Is there some way to simply separate the inputs with devise and make sure the end up in the right place?
I think, instead of simply commenting on an answer that led me to the final answer, I'll archive the answer here in case someone in the future is trying to also find this answer:
I will be assuming that you have some sort of setup as I do above.
First step is you need to modify your User controller to accept_nested_attributes_for the profile reference as well as add a utility method to the model so when requested in code, the application can either retrieve the built profile model or build one.
The user model ends up looking like so:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
accepts_nested_attributes_for :user_profile
def user_profile
super || build_user_profile
end
end
Secondly, you will need to modify your sign up/account_update form to be able to pass the attributes for this secondary model into the controller and eventually to be able to build the profile for the parent model.
You can do this by using f.fields_for.
Add something like this to your form:
<%= f.fields_for :user_profile do |user_profile_form| %>
<%= user_profile_form.text_field :attribute %>
<% end %>
An example of this in my specific case is:
<%= f.fields_for :user_profile do |user_profile_form| %>
<div class="form-group">
<%= user_profile_form.text_field :username, class: "form-control", placeholder: "Username" %>
</div>
<% end %>
Finally, you will need to tell Devise that it should accept this new hash of arguments and pass it to the model.
If you have created your own RegistrationsController and extended Devise's, it should look similar to this:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:email, :password, user_profile_attributes: :username)
end
end
(Of course, make the proper changes for your specific use-case.)
If you have simply added the Devise sanitization methods to your application controller, it should look similar to this:
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {|u|
u.permit(:email, :password, user_profile_attributes: :username)}
end
end
(Again, make the proper changes for your specific use-case.)
A small note on user_profile_attributes: :username:
Note this is a hash, of course. If you have more than one attribute you are passing in, say, as an account_update (hint hint), you will need to pass them like so user_profile_attributes: [:attribute_1, :attribute_2, :attribute_3].
Please check out the RailsCasts.com web-site.
There are a couple of interesting railscasts about nested model forms:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
http://railscasts.com/episodes/196-nested-model-form-revised
Also check out accepts_nested_attributes_for
Or check out this question:
Profile model for Devise users?
Also note that for Devise 4.2 the '.for' method for the devise_parameter_sanitizer is deprecated in favor of '.permit'
From the documentation:
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_in) do |user_params|
user_params.permit(:username, :email)
end
end
i feel like i am missing something obvious, but hopefully someone can help me out quickly.
i am trying to create a friendly_id slug for my User Model (using Devise). Here is my model:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :username, use: [:slugged, :finders]
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :username, :uniqueness => {:case_sensitive => false},
:format => { with: /\A[a-zA-Z0-9]+\Z/ }
end
In my UserController:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
end
When I load up the show method in my browser, it still has localhost:3000/user/3 - it does not show the slug. I know I have everything working because I have the same code in my Post Model and it works perfectly. Is there something I am missing with the User Validation / Devise that is causing it not to work?
Any help appreciated, thanks!
The reason is slug not saved. I think you are trying to find a legacy record which is saved before you initialized the friendly_id. But although you can generate slug and generate friendly-id for your record. Here is the process.
def should_generate_new_friendly_id?
new_record? || slug.nil? || slug.blank? # you can add more condition here
end
after adding this method, just trying save all the record again. Friendly will call the method and will update the slug if condition matched you defined inside that block.
User.find_each(&:save!)
In your view file write the url like:
<%= link_to "USER", current_user %>
or
<%= link_to "USER", user_path(current_user.to_param) %>
Please also make sure you don't have the username blank. If there is any chance that username can be blank you can use a dynamic method where you can set the column conditionally to use for generating slug. I think that should be all!
Thank you!
I have a User model (generated by devise) and a Submission model in a Rails project. I've added a field called 'full_name' to the user model. Also, there is a field for 'user_id' in the submission model.
I want to show the the full_name of the user associated with the submission on the submission show page. Right now it shows the user_id from the submission model just fine.
The User model I have this:
class User < ActiveRecord::Base
has_many :submissions
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
attr_accessible :email, :password, :password_confirmation, :remember_me, :full_name, :role_id
validates_presence_of :full_name
end
This is the model for submissions:
class Submission < ActiveRecord::Base
belongs_to :user
attr_accessible :description, :title, :user_id
end
This is in the controller:
def show
#submission = Submission.find(params[:id])
#user = User.find(params[#submission.user_id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #submission }
end
end
What I get when I try to use this line in the view:
<%= #user.full_name %>
I get this error:
Couldn't find User without an ID
I've tried several variations and can't quite figure out what should be in the place of:
#user = User.find(params[#submission.user_id])
Assuming that you're populating the data correctly, you only need the following once you have looked up the submission.
#submission.user
If you think that the data is OK, then try the following in the Rails console.
> Submission.first.user
What do you see there?
The specific error that you are seeing is because this:
params[#submission.user_id]
is unlikely to ever have anything in it. If the user ID is "1" (for example) then you're asking for the value in the params hash that corresponds to the key "1".
I am trying out Devise for the first time. One of the things that I wanted to do is provide an interface for Admin users to create, find and edit users. Here's where I may have gone wrong.
I created a PeopleController class which inherits from ApplicationController that lists people and provides methods and views for creating and updating users. Everything works fine with one exception. When the admin user updates their own record, the session is cleared and they have to login again after saving it.
In this application I'm not using the registerable module. Only an admin user can create new users. What is the right way in devise to provide user management tools. Creating my own controller seems to have been the wrong path to take.
Thanks in advance for your help.
Thank you very much for the help. This is essentially exactly what I am doing. I discovered a clue that helped me solve the problem of the user's session being cleared when they edit their own record in this wiki:
https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password
This is the line I needed:
sign_in resource_name, resource, :bypass => true
This method is located in Devise::Controllers::Helpers so I did this in my controller.
class PeopleController < ApplicationController
include Devise::Controllers::Helpers
Then in my update method I call it only if the current_user.id equals the id that is being edited:
def update
#person = User.find(params[:id])
if #person.update_attributes(params[:user])
sign_in #person, :bypass => true if current_user.id == #person.id
redirect_to person_path(#person), :notice => "Successfully updated user."
else
render :action => 'edit'
end
end
Now if the current user edits their own record, the session is restored after it is saved.
Thanks again for your responses.
This is how I manage users in one of my apps. I have only one User class generated with
rails g devise User
to which I added a role column with this migration:
class AddRoleToUser < ActiveRecord::Migration
def change
add_column :users, :role, :string, :default => "client"
end
end
and my User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable, :lockable and :timeoutable
devise :database_authenticatable, :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
def admin?
self.role == "admin"
end
end
Then to create new users all you would have to do is provide a custom method in a controller (maybe even subclass Devise::RegistrationsController) like this:
# some_controller.rb
def custom_create_user
if current_user.admin?
User.create(:email => params[:email], password => params[:password])
redirect_to(some_path, :notice => 'sucessfully updated user.')
else
redirect_to(some_other_path, :notice => 'You are not authorized to do this.')
end
end