Transactions rolling back: user does not exist - ruby-on-rails

I am trying to add the username of an author to a post in my blog, in order to later be able to verify that the user attempting to alter the post is the original author of the post. However, it is not working, and is returning the error "Validation failed: User must exist", even though the current user does exist, based on the fact that it is displaying the username of the currently logged in user on the page.
Error log:
Validation failed: User must exist
#post = Post.new(post_params)
#post[:author] = current_user.username
->> if #post.save!
flash[:success] = "Post created."
redirect_to post_path(#post.id)
else
Application controller (where current_user is declared):
class ApplicationController < ActionController::Base
helper_method :current_user
helper_method :require_user
def current_user
return unless session[:user_id]
current_user ||= User.find(session[:user_id])
end
def require_user
redirect_to '/login' unless current_user
end
end
Post model:
class Post < ApplicationRecord
has_many :comments
belongs_to :category
belongs_to :user
end
Schema:
create_table "posts", force: :cascade do |t|
t.string "title"
t.integer "category_id"
t.string "author"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Error when I do what Vasilisa said:
ActiveRecord::AssociationTypeMismatch (User(#47015106648260) expected, got "test" which is an instance of String(#47015080074780)):
def create
#post = current_user.posts.build(post_params)
==> #post.author = current_user.username
if #post.save
flash[:success] = "Post created."
redirect_to post_path(#post.id)

The Post model belongs_to :user, in ROR 5 this association validates for presence automatically. That's why you're getting "Validation failed: User must exist". Looks like you want to store post's user as author in db.
Change your posts table in migration
def change
remove_column :posts, :author, :string
add_reference :posts, :author
end
In the Post model change belongs_to :user to belongs_to :author, class_name: 'User'
Add to the User model
has_many :posts, foreign_key: :author_id
After it you can just write current_user.posts in the controller
def create
#post = current_user.posts.build(post_params)
if #post.save
flash[:success] = "Post created."
redirect_to post_path(#post.id)
else
render :new
end
end
And please, read again about associations in Rails

Related

Unknown attribute error after renaming a foreign key

I have three user roles:
enum role: { staff: 0, clinician: 1, admin: 2 }
I had a user_id column in my patients table to store the id of the staff user who created the patient record. To improve the clarity of the name, I renamed the column from user_id to author_id and adjusted the relationship best I knew how to reference the change to the foreign key.
When I try to access /patients/new, I get the error:
unknown attribute 'user_id' for Patient.
The error specifically highlights this line in my new patients method:
#patient = current_user.patients.build
What am I doing incorrectly? Thanks for any help!
patients table:
create_table "patients", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "age"
t.integer "staff_clinician_id"
t.integer "author_id"
t.index ["author_id"], name: "index_patients_on_author_id"
t.index ["staff_clinician_id"], name: "index_patients_on_staff_clinician_id"
end
Patient Model
class Patient < ApplicationRecord
belongs_to :user, -> { where role: :staff }, foreign_key: 'author_id'
Staff User concern
require 'active_support/concern'
module StaffUser
extend ActiveSupport::Concern
included do
belongs_to :university
has_many :patients
has_many :referral_requests
validates :university_id, presence: true, if: :staff?
end
class_methods do
end
end
Here is my patients controller:
class PatientsController < ApplicationController
before_action :require_login
def new
#patient = current_user.patients.build
end
def index
authorize Patient
#patients = policy_scope(Patient)
end
def show
#patient = Patient.find(params[:id])
end
def edit
#patient = Patient.find(params[:id])
end
def update
#patients = Patient.all
#patient = Patient.find(params[:id])
if #patient.update_attributes(patient_params)
flash[:success] = "Patient Updated!"
render 'patients/index'
else
render "edit"
end
end
def create
#patient = current_user.patients.build(patient_params)
if #patient.save
flash[:success] = "Patient Created!"
redirect_to new_referral_request_path(patient_id: #patient.id)
else
Rails.logger.info(#patient.errors.inspect)
render 'patients/new'
end
end
private
def patient_params
params.require(:patient).permit(:age, :staff_clinician_id, :author_id, insurance_ids: [], gender_ids: [], concern_ids: [], race_ids: [])
end
end
Seems you have a User model with has_many :patients (I assume that's where you used your StaffUser concern). Rails infers that the foreign key on the association table is user_id. You need to change this to:
##user.rb
has_many :patients, foreign_key: "author_id"

How do I param the Controller Favorite Create method to have an index#view

I want to create a polymorphic model to favorite each objects I want to create to stock in my user page.
I am developing a web app to learn japanese and we can favorite different types of cards as kanas or kanjis and sentences.
So there are 3 objects and soon more to favorite.
I migrated a table which names Favorite :
create_table "favorites", force: :cascade do |t|
t.integer "user_id"
t.integer "favoritable_id"
t.string "favoritable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Here is the Favorite model belongs_to
class Favorite < ActiveRecord::Base
belongs_to :favoritable, polymorphic: true
belongs_to :user
end
Here are the Cards model has_many
class Symbole < ActiveRecord::Base
accepts_nested_attributes_for :kanji_attribute, :allow_destroy => true
has_many :sentence_symboles, :class_name => "SentenceSymbole", :foreign_key => "symbole_id"
has_many :favorites, as: :favoritable
end
and I added in sentence model too
class Sentence < ActiveRecord::Base
include Authority::Abilities
has_many :sentence_symboles, :class_name => "SentenceSymbole", :foreign_key => "sentence_id", dependent: :destroy
has_many :favorites, as: :favoritable
end
Now here is the Favorite controller and I don't really know how to write the create method. Here is the Controller I do:
class FavoritesController < ApplicationController
def index
#favorites = Favorite.where(user: current_user)
end
def create
#Favorite.create(user_id: User.last.id, favoritable_id: Symbole.last.id, favoritable_type:"Symbole")
#favorite = current_user.favoritable.favorites.create(symbole: #symbole, sentence: #sentence).first
if #favorite.present?
#favorite.destroy
else
#favorite = current_user.favorites.new(symbole: #symbole, sentence: #sentence)
if not #symbole.favorites.where(user: current_user).take
#sentence.favorites.where(user: current_user).take
#favorite.save
end
end
# redirect_to favs_path
# redirect_to :back
respond_to do |format|
format.js { render :ajax_update_favs }
end
end
def destroy
#favorite = Favorite.find(params[:id])
#favorite.destroy
redirect_to :back
end
end
Please could someone give me the right way to favorite all object I want and add in an favorite index#view.
Thank you for your help.
I think my question is simple but no. How to favorite each object I want with the def Create controller what is the best method?
I do that
def create
#Favorite.create(user_id: User.last.id, favoritable_id: Symbole.last.id, favoritable_type:"Symbole")
#favorite = #favoritable.favorites.build(favorite_params)
#favorite.user = current_user
#favorite.save
respond_to do |format|
format.js { render :ajax_update_favorites }
end
end
Not sure I understood the problem entirely. It looks like you're overcomplicating favoritesController#create. If a record only should be favorited once, you should add a uniqueness validation in the Favorite model.
Assuming that you have your user model set up like
class User < ActiveRecord::Base
has_many :favorites
# Rest..
end
def create
#favorite = current_user.favorites.create(favorite_params)
# This will create a new Favorite with user_id: current_user.id, favoritable_type: "Symbole", favoritable_id: 1337
# Is this the desired behaviour?
respond_to do |format|
format.js { render :ajax_update_favs }
end
end
private
def favorite_params
params.require(:favorite).permit(:favoritable_type, :favoritable_id)
end
If this is called from javascript with jquery pass the type and id that you wan't to favorite.
$.post( "/favorites", {
favorites: {
favoritable_type: "Symbole",
favoritable_id: 1337
}
});

Unknown attribute 'user_id' for Comment works locally but not on Heroku - Rails

Locally i can create comment just fine but on heroku i get this error
ActiveRecord::UnknownAttributeError (unknown attribute 'user_id' for Comment.):
app[web.1]: app/controllers/blog/comments_controller.rb:9:in `create'
Parameters: {"utf8"=>"✓", "authenticity_token"=>"g83t8MpETbolCJq0vYku4SbnaZAro5ggXUI1PGVFJ18xneGcGCWQPjXHTfgBKBIP2/5WqHjfxf2d12+Pyq4PNA==", "comment"=>{"name"=>"test", "email"=>"tester#kostis.com", "body"=>"teste", "user_id"=>"1", "post_id"=>"1"}, "commit"=>"Create comment"}
here is my controller
class Blog::CommentsController < Blog::BaseController
def new
#comment = Comment.new
end
def create
#comment = Comment.new(comment_params)
if #comment.save
flash[:success] = "Comment successfully created!"
redirect_to blog_post_url(#comment.post)
else
flash[:warning] = "There was an error with your comment. Please scroll down to see the errors."
#post = #comment.post
render 'blog/posts/show'
end
end
def edit
#comment = Comment.find_by(id: params[:id])
end
def update
#comment = Comment.find_by(id: params[:id])
if #comment.update(comment_params)
flash[:success] = "Successfully updated..."
redirect_to blog_post_path(#comment.post)
else
render :edit
end
end
private
def comment_params
params.require(:comment).permit(:body,:name,:email,:user_id,:post_id)
end
end
UPDATE:
my local migration file is this
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :name
t.string :email
t.text :body
t.boolean :spam, default: false
t.references :post, index: true, foreign_key: true
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
but by running rails c on heroku and checking the actualy "Comment" structure there is no user_id, how is that possible?
If the user owns the comment: (comment belongs_to: :user)
Load the user:
user = User.find([id])
Then create the comment:
user.comments.new(comment_params)
If the post owns the comment: (comment belongs_to: :post)
Load the user:
post = Post.find([id])
Then create the comment:
post.comments.new(comment_params)
Try not to allow the client to say who they are - someone could easily fake that they are a different user then who they say. JWT is a good solution for this.
Maybe you forgot about this (if it works locally, as you say)?
heroku run rake db:migrate

How do I implement this direct messaging feature in a Twitter style app using Rails?

I'm trying to implement a direct messaging feature that is quite similar to Twitter using Rails, and I was wondering what I am doing wrong here. I am trying to restrict direct messaging to both followers and users that a user is following, but I can only restrict the direct messaging to followers, but for some reason can't add this feature for the users a user is following.
This is the relevant code in: app/controllers/messages_controller.rb
class MessagesController < ApplicationController
before_action :signed_in_user
before_action :find_user_id, only: :create
def create
#message = current_user.messages.build(message_params)
#message.recipient_id = #recipient.id
if #message.save
flash[:success] = "You successfully sent a message to #{#message.recipient.username}!"
redirect_to messages_path
else
flash[:error] = "There was an error sending your message, try again!"
redirect_to messages_path
end
end
private
def message_params
params.require(:message).permit(:recipient_id, :content)
end
def find_user_id
username = params[:message][:recipient].downcase
#recipient = User.where('lower(username) = ?', username).first
if #recipient.nil?
flash[:error] = "Invalid username"
redirect_to messages_path
elsif #recipient.following?(current_user) == nil || #recipient.follower?(current_user) == nil
flash[:error] =
"You can only direct message followers or users you are following"
redirect_to messages_path
end
end
end
The problem area specifically seems to be this line of code here:
elsif #recipient.following?(current_user) == nil || #recipient.follower?(current_user) == nil
elsif #recipient.following?(current_user) == nil works properly, but the latter code doesn't. The code from those two lines are from: app/models/user.rb
def follower?(other_user)
relationships.find_by(follower_id: other_user.id)
end
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
If anybody can help me out or suggest some possible fixes to this problem it'd be greatly appreciated. If you need more information to understand the code or to fix the problem, please don't hesitate to ask!
Here's some additional information for understanding the data model behind this:
In the db/migrate folder:
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
Also in the same directory:
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.integer :sender_id
t.integer :recipient_id
t.string :content
t.timestamps
end
add_index :messages, [:sender_id, :recipient_id, :content]
end
end
Thank you.

How to make Users automatically Follow Admin User on Sign Up

Currently I allow users to follow one another on my rails app (similar to twitter).
I would love if New Users that sign up to the site Automatically follow Admin User.
Similar to how MySpace use to automatically make Tom your first friend
Below is the code I use to create new users and allow users to follow one another.(i know this is a very broad question but .....)
(Can someone please point me in the right direction onto how I can get this started. Would I need to create a method....in my models or add code to the controller?)
New to Rails Please help)... :)
USER CONTROLLER
class UsersController < ApplicationController
before_filter :admin_user, only: [:destroy]
respond_to :html, :js
def new
#user = RegularUser.new
end
def index
#users = User.paginate(page: params[:page], :per_page => 100).search(params[:search])
end
def destroy
User.find_by_username(params[:id]).destroy
flash[:success] = "User destroyed."
redirect_to users_url
end
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
UserMailer.registration_confirmation(#user).deliver
UserMailer.welcome_user(#user).deliver
sign_in #user
flash[:success] = "Welcome to the ClickOnComics!"
redirect_to (publishers_path)
else
render 'new'
end
end
private
def admin_user
redirect_to(root_path) unless current_user.admin?
end
def follow_admins
admins = User.find_by_admin(true)
admins.each do |admin|
self.follow!(admin)
end
end
class RelationshipsController < ApplicationController
before_filter :current_user
respond_to :html, :js
def create
#user = User.find(params[:relationship][:followed_id])
current_user.follow!(#user)
respond_with #user
end
def destroy
#user = Relationship.find(params[:id]).followed
current_user.unfollow!(#user)
respond_with #user
end
end
MODELS
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
after_create :follow_admins
def follow_admins
admins = User.find_all_by_admin(true)
admins.each do |admin|
self.follow!(admin)
end
end
def following?(other_user)
relationships.find_by_followed_id(other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by_followed_id(other_user.id).destroy
end
end
I used this tutorial to help me establish privileged administrative users with a boolean admin attribute in the User model
http://ruby.railstutorial.org/chapters/updating-showing-and-deleting-users#sec-administrative_users
SCHEMA
create_table "users", :force => true do |t|
t.string "name"
t.string "email"
t.string "role"
t.string "username"
t.timestamp "created_at", :null => false
t.timestamp "updated_at", :null => false
t.boolean "admin", :default => false
t.string "password_reset_token"
t.timestamp "password_reset_sent_at"
end
Would I need to create a Method that defines user_admin?
In user.rb add a after_create filter
after_create :follow_admin!
def follow_admin!
relationships.create!(followed_id: admin_user.id)
end
In create action before sign_in add
#user.follow! admin_user
you need to fetch admin user first somehow.
Good idea would be to make follow!, following? and unfollow! methods to accept either id or object as in
def follow!(user_or_id)
id = (user_or_id.is_a?(User) ? user_or_id.id : user_or_id)
relationships.create!(followed_id: id)
end

Resources