ActiveModel::MissingAttributeError (can't write unknown attribute `user_id`) - ruby-on-rails

I have problems with my comments when my app is deployed. Locally everything is working. The logs from heroku says:
ActiveModel::MissingAttributeError (can't write unknown attribute `user_id`):
2018-01-02T15:52:43.461794+00:00 app[web.1]: [79cd7190-e2d9-4dd0-bf71-
709552e5c6e5] app/controllers/comments_controller.rb:15:in `create'
I have no ideas what is occuring the error. Maybe some database thing?
My CommentsController
class CommentsController < ApplicationController
def create
#post =Post.find(params[:post_id])
#comment =#post.comments.create(params[:comment].permit(:name, :body).merge(user: current_user))
redirect_to post_path(#post)
end
def destroy
#post = Post.find(params[:post_id])
#comment= #post.comments.find(params[:id])
if current_user.id == #comment.user_id
#comment.destroy
end
redirect_to post_path(#post)
end
end
My Models
class User < ApplicationRecord
has_many :posts
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
class Post < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
validates :title, presence: true, length: {minimum: 5}
validates :body, presence: true
end
class Comment < ApplicationRecord
belongs_to :post
belongs_to :user
end
My migration-file
class CreateComments < ActiveRecord::Migration[5.1]
def change
create_table :comments do |t|
t.string :name
t.text :body
t.references :post, index: true
t.timestamps
end
end
end
if you need more code or have any ideas please let me know
EDIT: if i add a user_id column i get a SQLite3::SQLException: duplicate column name: user_id: ALTER TABLE "comments" ADD "user_id" integer error
My schema.rb
`create_table "comments", force: :cascade do |t|
t.string "name"
t.text "body"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.index ["post_id"], name: "index_comments_on_post_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.string "theme"
t.integer "user_id"
end
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
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

You'll need to add a user_id column to your comments table. The belongs_to requires this. You're also going to need a post_id column, and user_id for your posts table to.
You can customise the column name, but the convention is to use the format parent_table_id.
Here's the key quote, from the docs:
Associations are implemented using macro-style calls, so that you can
declaratively add features to your models. For example, by declaring
that one model belongs_to another, you instruct Rails to maintain
Primary Key-Foreign Key information between instances of the two
models, and you also get a number of utility methods added to your
model.
This means, for example, if your first user has an id of 1, all of their comments and posts will have a user_id value of 1, which does the actual tying together of the records.
Here's an example migration with the relevant line included:
def change
create_table :comments do |t|
...
t.belongs_to :user, index: true
...
end
end
Does that make sense? Let me know if you've any questions and I can update as needed :)

You need to add user_id
Create the migration with
rails g migration AddUserIdToComment user:references
Then do
rake db:migrate
And you should be fine.

Your migration have missing
t.references :user, index: true
So you need to add user_id column within comments table
Update : It seems like you have some migration problem. I suggest you to check for rake db:migrate:status comment and look for any down migration. Once all are up then just run rake db:migrate:down VERSION='VERSION_NUMBER_HERE' and add your user t.references :user, index: true to the same migration and migrate.
PS: Change existing migration if and only if you have not pushed it.

Related

ActiveRecord::NotNullViolation in ReviewsController#create

Receiving this error in Rails:
PG::NotNullViolation: ERROR: null value in column "reviewer_id" violates not-null constraint DETAIL: Failing row contains (26, 2222, 2222, 8, null, 2021-01-30 19:26:03.354983, 2021-01-30 19:26:03.354983).
This app is trying to allow users to give other users reviews.
I am new, so the error may be quite obvious, but not to the untrained eye. After hours of research, this was my last resort.
I'd appreciate in help fixing this. God bless.
routes.rb
resources :users do
resources :reviews, only: [ :new, :create ]
end
reviews_controller.rb
def create
#review = Review.new(review_params)
#review.user_id = current_user.id
if #review.save
redirect_to user_path(current_user), notice: 'Review added!'
else
render :new
end
end
private
def review_params
params.require(:review).permit(:rating, :content)
end
schema.rb
create_table "reviews", force: :cascade do |t|
t.text "content"
t.integer "rating"
t.bigint "user_id", null: false
t.bigint "reviewer_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["reviewer_id"], name: "index_reviews_on_reviewer_id"
t.index ["user_id"], name: "index_reviews_on_user_id"
end
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "username"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
add_foreign_key "offers", "games"
add_foreign_key "offers", "users"
add_foreign_key "rentals", "offers"
add_foreign_key "rentals", "users"
add_foreign_key "reviews", "users"
add_foreign_key "reviews", "users", column: "reviewer_id"
user.rb
has_many :reviews, dependent: :destroy
has_many :given_reviews, source: :reviews, foreign_key: :reviewer_id
has_many :received_reviews, source: :reviews, foreign_key: :user_id
review.rb
belongs_to :user
validates :content, presence: true
validates :rating, presence: true
You have a database constraint on Review, for the reviewer_id field, defined in your schema here:
t.bigint "reviewer_id", null: false
In your controller#create action you are defining a user_id, but I don't see where you are passing in the reviewer_id or setting it. If you have a field in the form, you'll need to add that param to the review_params so it can be passed from the form to the controller action. Or you'll need to define it in the create action similar to how you've defined the user_id.
Since your reviewer is the person leaving the review, and the User is being reviewed, I would do this:
In your view where you are leaving the review, (I assume this would be User#show) you are viewing a specific user. You know which user this is and most likely in that view it is defined as #user, so you can most likely use <%= hidden_field_tag :user_id, #user.id %> in the form for the review. This will pass user_id to the Reviews#create action in the params. You will also need to add user_id to review_params so it can be accessed in the #create action.
Then in your Reviews#create action, assign #review.reviewer_id = current_user.id. #review.user_id will be assigned when you call Review.create(review_params) assuming you have added it the the form and the review_params as suggested above.
Try adding a breakpoint in the create action and look at the params object getting sent from the review form, this may be helpful for you to understand what's happening.
Typical Rails practice is to use a model validation as well. This way you'd get an app error before the request ever hits the database.

When I add has_many, some data is suddently not saved

I have 3 models, Challenge, Pun, User (managed by Clearance gem)
A User can create a Challenge. A Challenge contains many puns. A User can also create a Pun.
Everything is fine until I set a Pun to belong_to a User, then suddenly Puns are no longer saved.
class User < ApplicationRecord
include Clearance::User
has_many :challenges
has_many :puns
end
class Challenge < ApplicationRecord
has_many :puns, :dependent => :delete_all
belongs_to :user
end
class Pun < ApplicationRecord
belongs_to :challenge
belongs_to :user
end
In my PunController I have tried to establish the current_user id
def create
#pun = #challenge.puns.create(pun_params)
#pun.user_id = current_user.id if current_user
redirect_to #challenge
end
private
def set_challenge
#challenge = Challenge.find(params[:challenge_id])
end
def pun_params
params[:pun].permit(:pun_text,:user_id)
end
What am I doing wrong? I'm trying to keep it as simple as possible, but seems like Users don't want to be associated with more than one thing, particularly if nested. Is this a Clearance issue?
DB setup:
create_table "challenges", force: :cascade do |t|
t.text "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.datetime "start_time"
t.datetime "end_time"
t.bigint "user_id"
t.index ["user_id"], name: "index_challenges_on_user_id"
end
create_table "puns", force: :cascade do |t|
t.text "pun_text"
t.bigint "challenge_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "user_id"
t.index ["challenge_id"], name: "index_puns_on_challenge_id"
t.index ["user_id"], name: "index_puns_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "tagline"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "encrypted_password", limit: 128
t.string "confirmation_token", limit: 128
t.string "remember_token", limit: 128
t.index ["email"], name: "index_users_on_email"
t.index ["remember_token"], name: "index_users_on_remember_token"
end
Well in you currrent code you don't save user_id after setting it. And if you do not expect creation to fail you can do "create!".
So you can try:
def create
#challenge.puns.create!(pun_params.merge(user_id: current_user.id))
redirect_to #challenge
end
You can do this using simply hidden_field like in the form
<%= object.hidden_field :user_id, value: current_user.id %>
it won't work without user session because the relationship does not optional, and remove this line from the controller
#pun.user_id = current_user.id if current_user
and redirect
redirect_to #pun
it will work

How would I go about allowing users to create surveys

Iv'e gotten myself into a bit of a brain mess up these past two days. I'd like to be able to allow my users to create a campaign (same concept as surveys), it will allow them to request certain data they wish such as an email address. This will then allow the person completing the form to proceed and receive a download link after entering an email. The email entered should be stored for the person who created the campaign to view.
Iv'e taken the approach with nested forms, however I ran into the trouble of allowing emails to be entered and saved for the campaign creator to view.
Any help is appreciated, thanks.
campaign.rb model
class Campaign < ActiveRecord::Base
belongs_to :user
has_many :queries
accepts_nested_attributes_for :queries
end
query.rb model
class Query < ActiveRecord::Base
belongs_to :campaign
has_many :results
end
result.rb model
class Result < ActiveRecord::Base
attr_accessible :content, :email, :query_id
belongs_to :query
end
schema.rb
create_table "campaigns", force: :cascade do |t|
t.string "title"
t.text "description"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "campaigns", ["user_id"], name: "index_campaigns_on_user_id", using: :btree
create_table "queries", force: :cascade do |t|
t.integer "campaign_id"
t.text "content"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "queries", ["campaign_id"], name: "index_queries_on_campaign_id", using: :btree
create_table "results", force: :cascade do |t|
t.integer "query_id"
t.text "content"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "results", ["query_id"], name: "index_results_on_query_id", using: :btree
Part of campaign_controller.rb
private
# Use callbacks to share common setup or constraints between actions.
def set_campaign
#campaign = Campaign.find(params[:id])
end
def campaign_params
params.require(:campaign).permit(:title, :description, :queries_attributes)
end
def query_params
params.require(:query).permit(:content, :email, :campaign_id)
end

Users, form and tasks association rails

I have an app allowing a user to fill a form (named "checklist") and then have a list of tasks he will have to do. The tasks (named "advices") are related to the answers that the user gave in the form.
For example, if a question is "have you cooked dinner ?" and the user answers "no", then an advice "go cook dinner" will be displayed.
Once a advice is done, the user can mark it as completed. Advices are the same for all users. They already are created in the app by admin.
So users have a checklist, checklist belongs to a user.
The problem I encounter is : when a user marks an advice as completed, it is marked as completed for all users. That should not be.
I am not really sure how to fix this. Associations "Has-many", and "Belongs_to" between advices and users should not work since the user does not create the advices ?
I am new to rails so I would be happy if someone could help.
Note that I use Devise to manage users.
Schema :
ActiveRecord::Schema.define(version: 20160407143608) do
create_table "advices", force: :cascade do |t|
t.string "name"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "category_id"
t.boolean "status"
t.string "linkname1"
t.text "link1"
t.text "link2"
t.string "linkname2"
t.text "link3"
t.string "linkname3"
t.integer "ref"
t.boolean "completed"
end
create_table "categories", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "checklists", force: :cascade do |t|
t.boolean "facebook"
t.boolean "twitter"
t.boolean "linkedin"
t.boolean "viadeo"
t.boolean "instagram"
t.boolean "community"
t.boolean "cms"
t.boolean "seo"
t.boolean "crowdfunding"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
add_index "checklists", ["user_id"], name: "index_checklists_on_user_id"
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
Models :
class Advice < ActiveRecord::Base
belongs_to :category
end
class Checklist < ActiveRecord::Base
belongs_to :user
end
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 :checklists
end
View :
<%= advice.name %> | <%= link_to "Completed", complete_advices_path(advice), method: :put %>
controller :
def complete
#advice.completed = true
#advice.save
redirect_to root_path
end
You need a join model.
$ rails g model UserAdvice user:references advice:references
class UserAdvice
belongs_to :user
belongs_to :advice
end
in user.rb
has_many :user_advices
has_many :advices, through: :user_advices
in advice.rb
has_many :user_advices
has_many :users, through: :user_advices
Create a record in the join model when something gets checked off and then query that table to make sure the task is done for an individual user.
So, when a user checks off a task and submits, instead of using the completed boolean, you'd actually create a record that has the advice_id and the user_id. Then if that record exist for that advice, it should be checked off for that user. Does that make sense?
If you were hiding the completed tasks from users who had completed them, for instance, you could say something like
<% if UserAdvice.where(user_id: current_user.id, advice_id: advice.id).count > 0 %>
This will work, and is fine at first, but doing it this way could slow down your app, though. If there are a lot of advices, what you'd probably want to do is run the query once and get all the user_advice records and pluck the ids. Then check against that array of ids against the individual record.
In your controller
# this will return an array of advice_ids
#user_advices = UserAdvice.where(user_id: current_user).pluck(:advice_id)
Then, as you iterate through advices in your view:
<% unless #user_advices.include?(advice.id) %>
show the advice
<% end %>
EDIT:
To create the record inside that complete action:
def complete
UserAdvice.create(user_id: current_user.id, advice_id: #advice.id)
redirect_to root_path
end

Ruby on Rails SQLite3::ConstraintException: NOT NULL constraint failed:

I am developing a simple app where a user can add a subject to a cart. Before I add the authentication I was able to add a subject to the cart but as I want a user to be able to has access to just his/her cart I used Devise to create User with authentication. Now, when I click on the button to add a subject to the cart I have the following error:
This is a snapshot of the error I get: 1
SQLite3::ConstraintException: NOT NULL constraint failed: carts.user_id: INSERT INTO "carts" ("created_at", "updated_at") VALUES (?, ?)
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_cart
Cart.find(params[:user_id])
rescue ActiveRecord::RecordNotFound
cart = Cart.create
params[:user_id] = cart.id
cart
end
end
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_one :cart
end
class Cart < ActiveRecord::Base
belongs_to :user
has_many :line_items, dependent: :destroy
scope :user_carts, ->(user) { where(['user_id= ?', user.id]) }
end
class AddUsersToCarts < ActiveRecord::Migration
def up
add_reference :carts, :user, index: true
Cart.reset_column_information
user = User.first
Cart.all.each do |cart|
cart.user_id = user.id
cart.save!
end
change_column_null :carts, :user_id, false
add_foreign_key :carts, :users
end
def down
remove_foreign_key :carts, :users
remove_reference :carts, :user, index: true
end
end
Edit: I added the schema.rb below:
ActiveRecord::Schema.define(version: 20151210213408) do
create_table "carts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", null: false
end
add_index "carts", ["user_id"], name: "index_carts_on_user_id"
create_table "line_items", force: :cascade do |t|
t.integer "subject_id"
t.integer "cart_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "subjects", force: :cascade do |t|
t.string "title", null: false
t.string "code", null: false
t.text "description", null: false
t.integer "credits", null: false
t.string "lecturer", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "subjects", ["title"], name: "index_subjects_on_title", unique: true
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.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
You current_cart method does not make much sense.
You cannot find the user's cart by calling Cart.find(params[:user_id]), because that looks for a cart by an id (not by an user_id).
Cart.create fails, because you do not provide an user_id that is required (your database migrations says that the filed cannot be null).
Furthermore, params[:user_id] = cart.id changes the params hash, but not the new cart.
Change that method to something like this (using find_or_create_by) and use the current_user.id instead of params[:user_id]:
def current_cart
Cart.find_or_create_by(user_id: current_user.id)
end

Resources