I have a post function in my app that takes in the following parameters: a title, the content of the post, a boolean for anonymity, a boolean for graphic content and a string to record the username of the user who made the post. I have all of these fields specified in the new post form of my rails app. Unfortunately, when I go to submit the post, I receive an error saying: User must exist
. This is happening because the User parameter is not getting posted even though I have added the parameter in my controller:
#app/controllers/posts_controller.rb
def post_params
params.require(:post).permit(:title, :content, :anonymous, :graphic, :user_name);
end
My schema also has a parameter for the username:
#db/schema.rb
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name" # here is the parameter for name
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
I used embedded ruby to supply the name parameter in my post form:
<div >
<%= form.text_field :user_name, name: :post_user_name, value:current_user.name, type: :hidden %>
</div>
I used inspected element in my browser and it showed that the value was equal to my username so I'm stuck as to where the error is. I had a look at this tutorial where the person did a similar thing. He got the same error but he fixed it by adding another permit to his parameters in app/controllers/posts_controller.rb
.
My create method looks like this:
#app/controllers/posts_controller.rb
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.html { redirect_to post_url(@post), notice: "Post was successfully created." }
format.json { render :show, status: :created, location: @post }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @post.errors, status: :unprocessable_entity }
end
end
end
I have also added has_many :posts
in my user model as well as belongs_to :user
in my post model.
I am making a forum app, like reddit. I want to have an edit function for each post. The catch is that I want each user to only be able to edit their own posts, thus I need each post to 'remember' the user it was posted by.
My post table contains the following columns:
create_table "posts", force: :cascade do |t|
t.string "title"
t.string "content"
t.boolean "anonymous"
t.boolean "graphic"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "user_name"
t.index ["user_name"], name: "index_posts_on_user_name"
end
When I change @post.user = current_user
in my create method, to @post.user_name = current_user
, I get the "User must exist" error
CodePudding user response:
Its likely that you never set the @post.user
and therefore you will get a for ActiveRecord::RecordInvalid, meaning the post cant exist without a user attached.
since you are using devise you have access to a helper method called current_user
(which acts as the logged in/authenticated user). If you indeed want the created post to be attached to the user who you are logged in as then all you need to do is:
def create
@post = Post.new(post_params)
@post.user = current_user
# your other code below
end
CodePudding user response:
Let start from the top here. You don't want to be setting up assocations to anything but the primary key column unless you have an extremely important reason to so. Which isn't the case here - and also your users table doesn't even have a user name.
Ditch that user_name
column and input and add a user_id
column to the table:
class AddUserToPosts < ActiveRecord::Migration[7.0]
def change
add_reference :posts, :user, null: true
remove_index :posts, :user_name
remove_column :posts, :user_name
end
end
If you want to allow posts that are not assocatiated with a user you need to make the column nullable and make the association optional:
class User < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user, optional: true
end
Creating resources that belong to the current user is an extremely common task and the correct way to do it is always to get the user from the session (or a token) as they are encrypted and difficult to tamper with. With Devise you do this with the current_user
method.
If you instead add a hidden input with a user id or a user name you're just asking for spoofing as its trivial to input any users id with the web inspector or Postman.
def create
@post = if user_signed_in?
current_user.posts.new(post_params)
else
Post.new(post_params)
end
# ...
end
Make sure you do not permit the :user_id
attribute in your params whitelist.