Using a model in the controller for a different model? - ruby-on-rails

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

Related

Rails devise doesn't redirect to root path

My user model looks like this in which type is column in user table and std is used just to get additional data at the time of user sign_up.Now when user(type: Student) login, devise again rendered the login page with message log in successful doesn't render the root_path and in log it is showing rollback transaction but when i refreshed the page then it render to root_path. This problem is only happening for type:Student and when i remove validates_presence_of :std line everything is running perfectly.
Now the question is why this is happening or how it can be done
?
class User < ApplicationRecord
attr_accessor :std
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :type
validates_presence_of :std , allow_blank: false , if: -> { type == 'Student' },
message: 'Student must add grade to continue'
end
Devise::RegistrationsController.rb
class RegistrationsController < Devise::RegistrationsController
def create
super
if params[:user][:type] == 'Student' and user_signed_in?
current_user.grade = Grade.new({cls: params[:user][:std].to_i})
current_user.save
end
end
private
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation, :type, :std)
end
end
Solved.. I think it is also validating user(type:Student)at the time of login so i just added
validates_presence_of :std , allow_blank: false , if: -> { type == 'Student' and new_record? },
message: 'Student must add grade to continue'
Now it is only validating at the time of signup only.

Rails - Devise - Add profile information to separate table

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

How to associate a Devise User with another existing model?

I am very new to Ruby on Rails and have setup Devise for authentication. I have an existing model that I created prior to adding Devise. That model is called Article. I believe I have done everything I need to do in order to use the association=(associate) method that "assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object’s foreign key to the same value" which is exactly what I need to do.
Here is Devise's User model:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
has_one :article
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
Here is my Article model:
class Article < ActiveRecord::Base
belongs_to :user
validates :name, presence: true, length: { minimum: 5 }
end
Here is my migration:
class AddUserRefToArticles < ActiveRecord::Migration
def change
add_reference :articles, :user, index: true
end
end
Here is my create method from my articles_controller.rb:
def create
#article.user = current_user
#article = Article.new(post_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
And here is what happens when my controller runs:
NoMethodError in ArticlesController#create
undefined method `user=' for nil:NilClass
The highlighted code is #article.user = current_user. I was at least glad to know that I wrote that line of code similar to the popular answer in the Devise how to associate current user to post? question that I saw on here before posting this one.
I know I'm making a rookie mistake. What is it?
A new User instance needs to be assigned to #article before you can access any of the instance's attributes/associations. Try the following:
#article = Article.new(post_params) # Assign first
#article.user = current_user # Then access attributes/associations
The code posted in the question yields a nil:NilClassexception because the user association is being invoked on #article, which is nil because nothing has yet been assigned to it.

Rails: undefined method `primary_key' for ActiveSupport::HashWithIndifferentAccess:Class

I have a polymorphic relationship for my User model. I am using Devise.
When I try to edit the user's details, I get the following error:
undefined method `primary_key' for ActiveSupport::HashWithIndifferentAccess:Class
The data submitted through the form is:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"Ap0JP2bs/w9J6iI9rZahiKR1K8UEIi7rp33a4OutMbo=",
"user"=>{"email"=>"some_email#yahoo.com",
"rolable"=>{"first_name"=>"Christopher",
"last_name"=>"Columbus",
"city"=>"16"}},
"commit"=>"Update"}
The controller method is:
def update
#user = User.find(current_user.id)
if #user.update_attributes(params[:user])
redirect_to edit_user_registration_path, notice: 'Your profile was successfully updated.'
else
redirect_to edit_user_registration_path, error: "Something went wrong, couldn't update your profile!."
end
end
The models:
1. User
class User < ActiveRecord::Base
belongs_to :rolable, :polymorphic => true
# Devise business
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :remote_avatar_url, :avatar, :login, :username, :email, :password, :password_confirmation, :remember_me
# For authenticating with facebook
attr_accessible :provider, :uid, :rolable
devise :omniauthable, :omniauth_providers => [:facebook]
# For authenticating with both username or email
attr_accessor :login
# Upload avatar
mount_uploader :avatar, AvatarUploader
# Makes username necessary
validates :username, :presence => true
end
2. Customer
class Customer < ActiveRecord::Base
has_one :user, :as => :rolable
has_one :preferences, :class_name => "CustomerPreferences"
belongs_to :city
attr_accessible :first_name, :last_name
end
What's the problem?
Based on your request hash, you are passing the rolable attribute as:
"rolable"=>{"first_name"=>"Cristian",
"last_name"=>"Gavrila",
"city"=>"16"}
You can't do this unless you specify that the User model accepts nested attributes for rolable. However, you have setup your User as belonging to a Rolable rather than the other way around. Nested attributes aren't designed to handle this inverse relationship - you may want to reconsider what you are trying to accomplish here, and modify the relationship accordingly.
For instance, you may want to turn your hash inside out and pass the rolable attributes with the user attributes embedded within. Or you may want to turn rolable into a has_one association.

Setting a default value when creating a new object of a model

Im learning Rails and I'm just wondering if some code I wrote is correct and safe. I have two models, a user and post model. The posts belong to users, so I want to pass the user_id automatically to post when the object is created. I used an assign_attributes method in the post controller to set the user_id using the current_user helper provided by devise. Below is my relevant code. Again I want to know if this is correct or if there is better way of doing it.
def create
#post = Post.new(params[:post])
#post.assign_attributes({:user_id => current_user.id})
end
Post Model
class Post < ActiveRecord::Base
attr_accessible :content, :title, :user_id
validates :content, :title, :presence => true
belongs_to :user
end
User model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me
has_many :posts
end
You're pretty close. Since you've 1) been provided the current_user convenience helper by Devise, and 2) configured User and Post as a has_many/belongs_to relationship, it makes sense to create the new post, then append it to current_user. Then, in your Post model, you'll want to break up validations for individual attributes – the way you've listed :content, :title in sequence won't work.
# app/controllers/posts_controller.rb
def create
post = Post.create(params[:post])
current_user.posts << post
end
# app/models/post.rb
class Post < ActiveRecord::Base
attr_accessible :content, :title, :user_id
validates :content, :presence => true
validates :title, :presence => true
belongs_to :user
end
I don't think that is necessary since you have already created the relationship between posts and users. If you nest the posts resources into the user, it will automatically create the relationship between the 2 models.
In routes.rb
resources :users do
resources :posts
end
With that done, you will now reference posts as #user.post. I have already shown an example in this question.
I would say something like this :
def create
params[:post][:user_id] = current_user.id
#post = Post.new(params[:post])
#post.save
end
or
def create
#post = Post.new(params[:post])
#post.user = current_user
if #post.save
...
else
...
end
end
or
def create
#post = #post.new(params[:post])
#post.user_id = current_user.id
#post.save
end
You could put the user_id in the params but that would not be safe. user_id should not be in 'attr_accessable' so it will be protected for mass_assignment.

Resources