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.
Related
I'm trying to add idea(s) to a user. So far I added has_many to my user and belongs_to to my idea. And I added a foreign key to User using:
rails g migration Add_User_id_To_Ideas user_id:integer
Now, how do I assign the idea to the specific user, when a user creates a new idea?
I tried to work parallel to this example, but I'm a bit stuck.
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :ideas
end
idea.rb
class Idea < ActiveRecord::Base
belongs_to :user
has_many :comments
mount_uploader :picture, PictureUploader
# {attr_accessor :Status}
enum status: [ :Draft, :Published]
end
Several ways to do this, but I think the most straight forward way would be to save the user_id in the ideas controller create action.
# def create inside ideas_controller
#idea.user_id = current_user.id
if #idea.save
# etc
Couple other options are a hidden field or before_save callback. Here's an example of passing through a hidden field. In your #idea form:
<%= f.hidden_field :user_id, value: current_user.id %>
This will add user_id to the params being saved. Make sure that :user_id is whitelisted in the permitted params at the bottom of your ideas controller.
To permit user_id in idea_params:
def idea_params
params.require(:idea).permit(:user_id, :also_add_other_params)
end
I have the following classes in my 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_one :list
end
and
class List < ActiveRecord::Base
belongs_to :user
end
Now I want to create a new list button on my list/show page where I can create a new list for a current_user.
def new
#list = List.new
end
def create
#list = current_user.list.build(params.require(:list).permit(:title))
if #list.save
redirect_to #list, notice: "List was saved successfully."
else
flash[:error] = "Error creating list. Please try again."
render :new
end
end
But something goes wrong in my create function. I makes sense cause also when I open rails c and try:
u = User.first => validated user
u = List.new
I get the error that list is not method for u (my user). What goes wrong?
You didn't provide information about error, but I guess interpreter complains about calling build method on nil. It's because you call:
#list = current_user.list.build(params.require(:list).permit(:title))
but current_user.list returns nil. You should have this instead:
#list = current_user.build_list(params.require(:list).permit(:title))
Documentation for has_one method
I am facing an error while trying to link the :username in my User table and my Room table. I made a custom auth with devise and added :username.
I would like the username to be the link between the User table from devise and my Room table.
I am trying to build this app to recreate a kind of airbnb but mainly as an exercise as I started programming in ruby few months ago.
I get the error:
ActiveRecord::RecordNotFound in RoomsController#new
Couldn't find User without an ID
line #19 #room.username = User.find(params[:username])
Thank you very much for your help. I am stuck in here for hours now :-(
rooms_controller
def new
#room = Room.new
#room.username = User.find(params[:username]) #error seems to come from here
end
routes.rb
Rails.application.routes.draw do
devise_for :users
get "home/info"
root :to => "home#info"
resources :rooms
resources :users do
resources :rooms
end
room.rb
class Room < ActiveRecord::Base
mount_uploader :photo, PictureUploader
belongs_to :user
validates_presence_of :username, :location, :description, :capacity, :price_day, :photo
end
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates_presence_of :username
validates_uniqueness_of :username
has_many :rooms
end
It should be something like this
def new
#room = Room.new
#room.username = User.find_by_username(params[:username])
end
If you just use .find() it expects the id of the user. Also see http://guides.rubyonrails.org/active_record_querying.html#dynamic-finders
There is a logic error in that you are saving #room.username to the User Object. You should be setting #room.user = User.find_by(...) OR #room.user_id = User.find_by(...).id
Active record will automagically make a method for you that will be #room.user.username if you want to get the username.
Now, here are the ways to find a user.
#room = Room.new #Then either of the following
#room.user = User.find_by(username: params[:username]) #Returns only one value
#room.user = User.find_by_username(params[:username]) #Returns only one value
#room.user = User.where(username: params[:username]) #Returns all users which meet condition.
As already mentioned in the answers, User.find() takes an ID. One thing to know is that all methods that start with .find for active record return a single record even if many meet the condition.
If you are having any problems still, then show us your Database Schema, and we can help further.
I found a solution. The Room is created with the the right :username and nothing is seen by the user.
In my Rooms controller, I kept
def new
#room = Room.new end
And I added this line in the "def create" part :
def create
#room = Room.new(room_params)
#room.username = current_user.username
Thank you for your help, this help me to understand better the relations in rails.
Have a nice day !
I edited devise's RegistrationsController::create method to modify slightly the behaviour. What I'm trying to do is that if there are no admin users in the database, the one that first signs up is going to be assigned the admin role, else it will be a regular user.
However, the role, though assigned correctly to the object (tested), it's not being persisted to the database.
Model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :role
Roles = [ :admin, :default ]
def is? requested_role
self.role == requested_role.to_s
end
def self.admin_role
return Roles[0]
end
def self.default_role
return Roles[1]
end
end
Modified devise method:
def create
build_resource(sign_up_params)
admin_user = User.find_by_role(User.admin_role)
if admin_user.nil?
resource.role = User.admin_role
else
resource.role = User.default_role
end
# here puts resource.role shows what's expected is indeed being assigned to the object
if resource.save
...
end
end
Why isn't the role being stored in the database? Why is it NULL?
You don't need the attr_accessor for :role if you have this defined as a column on your table. ActiveRecord gives you the database backed accessors just by having the relevant column defined in the relevant table.
Your attr_accessor will be overriding these and preventing them from persisting your changes to the database.
I have created a user model using devise. In the comment model I set the comment belongs_to :user. Within the user model, I then set the has_many: comments relationship.
comment model
class Comment < ActiveRecord::Base
belongs_to :user
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
has_many :comments
end
Inside the comment controller, I have the following code.
class CommentsController < ApplicationController
def create
#comment = Comment.new(params[:comment])
#comment.user = current_user
if #comment.save
end
end
......
end
In the view within the index page I have this code inside an erb basically to pull the name of the user
<td><%= comment.user.first_name if comment.user %></td>
but it's not displaying anything at all?
Basically I have tables in the database, the User table and Comment table. I would like to associate the id of the user from the User table with the foreign_key, user_id in the comment table.
For example, I have logged in as a user with user id = 1 and created a comment. When I queried the database to check the comment the user_id was nil
#<Comment id: 19, Ticker: "Hello", MyComment: "Test", created_at: "2014-06-07 20:07:44", updated_at: "2014-06-07 20:07:44", user_id: nil>
I think you should have this;
class CommentsController < ApplicationController
def create
#comment = Comment.new(params[:comment])
#comment.user_id = current_user.id
if #comment.save
end
end
......
end
Then successfully call this;
<td><%= comment.user.first_name if comment.user %></td>
This is because #comment contain only the user reference.