Rails help - unknown attribute 'creator_id' for Event - ruby-on-rails

class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :events, foreign_key: :creator_id, class_name: "Event"
end
class Event < ApplicationRecord
belongs_to :creator, class_name: "User", foreign_key: :creator_id
end
When I go to the events/new route I get this error: unknown attribute 'creator_id' for Event. I have done the database migration, I have added before_action's.
def new
#event = current_user.events.build(event_params)
end
Any ideas?
Update:
Status Migration ID Migration Name
--------------------------------------------------
up 20210610064055 Create events
up 20210610070312 Devise create users
up 20210612072338 Add body title to event
up 20210612091329 Add foreign key
**Further updates as requested in the comment section. **
Migrations file:
class AddForeignKey < ActiveRecord::Migration[6.1]
def change
add_column :users, :creator_id, :integer
end
end
Database schema file:
create_table "events", force: :cascade do |t|
t.date "date"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "title"
t.text "body"
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.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "creator_id"
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
Do I need to add a foreign key to the events table as well? I can see I have not done that which I believe is wrong...

A foreign key should be added to the table you're making a reference from. In this case you're making a reference from events to users. So creator_id would have to be added to events. And it references a specific users.id value. So you need to remove they foreign key from users and add it to events instead.
class AddForeignKey < ActiveRecord::Migration[6.1]
def change
add_column :events, :creator_id, :integer
end
end
Also, no need to specify the foreign key in the rails model specifically. The rails guides state:
By convention, Rails guesses that the column used to hold the foreign
key on this model is the name of the association with the suffix _id
added. The :foreign_key option lets you set the name of the foreign
key directly:
So
class Event < ApplicationRecord
belongs_to :creator, class_name: "User"
end
Would suffice

Related

Custom email unique validation not working on devise

I need to validate email with scope but with devise it is not working. Even after modifying index for email uniqueness it is not allowing user to create on basis of scope.
i have tried adding following line on config/initializers/devise.rb
config.authentication_keys=[:email, :organization_id]
But it doesnot work.
Also i have tried with validation on model:
validates_uniqueness_of :email, scope: :organization_id
But it doesnot work.
Also tried by modifying user migration:
def up
remove_index :users, name: 'index_users_on_email'
add_index :users, [:email, :organization_id], unique: true
end
But it doesnot work as well.
Relation between user model an organization:
app/models/organization.rb
Class Organization < ApplicationRecord
has_many :users
end
app/models/user.rb
class User < ApplicationRecord
belongs_to :organization
end
Here is schema :
create_table "users", force: :cascade do |t|
t.string "type"
t.string "full_name"
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 "authentication_token", limit: 30
t.integer "organization_id"
t.string "archive_number"
t.datetime "archived_at"
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
t.index ["email" , "organization_id"], name: "index_users_on_email_and_organization_id", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
My problem was that:
I have user with email in organizatin 1 now is have to add another user in organization 2 with same email. While doing this i am getting error
ActiveRecord::RecordInvalid: Validation failed: Email has already been
taken
I belive that i should be able to add user with same email after adding scope under validation.
For the email unique validation, you can try this:
Define following in your model:
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
validates_uniqueness_of :email, scope: :organization
def will_save_change_to_email?
false
end
def email_changed?
false
end
end
This worked for me.

Setting up a has_many_through with separate database migrations rails 5

So I have the following functionality where I have courses, course modules and course exercises.
I have it where users can mark off modules once completed when all modules are completed the course gets set to complete.
However, this is applying to all users, not individual users. So, for example, what is currently happening is that one user completes the course and when it's being marked as complete but if I sign in as a second user (who hasn't completed the course) it's being marked as complete.
From my research, I believe I could achieve this using a has_many_through association, but I'm unsure how to set this up.
Here is how I have things set up so far.
schema.rb
create_table "course_exercises", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "video"
t.integer "course_module_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.index ["course_module_id"], name: "index_course_exercises_on_course_module_id"
t.index ["slug"], name: "index_course_exercises_on_slug", unique: true
end
create_table "course_modules", force: :cascade do |t|
t.string "title"
t.integer "course_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.boolean "complete", default: false, null: false
t.index ["course_id"], name: "index_course_modules_on_course_id"
t.index ["slug"], name: "index_course_modules_on_slug", unique: true
end
create_table "courses", force: :cascade do |t|
t.string "title"
t.text "summary"
t.text "description"
t.string "trailer"
t.integer "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.boolean "complete", default: false, null: false
t.index ["slug"], name: "index_courses_on_slug", unique: true
end
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
has_one_attached :avatar
has_many :courses
def after_confirmation
welcome_email
super
end
protected
def welcome_email
UserMailer.welcome_email(self).deliver
end
end
course.rb
class Course < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
has_many :users
has_many :course_modules
validates :title, :summary, :description, :trailer, :price, presence: true
def complete!
update_attribute(:complete, true)
end
end
course_module.rb
class CourseModule < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
belongs_to :course
has_many :course_exercises
validates :title, :course_id, presence: true
scope :completed, -> { where(complete: true) }
after_save :update_course, if: :complete?
private
def update_course
course.complete! if course.course_modules.all?(&:complete?)
end
end
Completed modules
Completed course
Databases:
Course
Course Modules
But as I mentioned above, it's getting assigned to all users, not individual users.
Any help here is appreciated.
As per the description it seems like you will be needing another table to
capture the data user wise to show completed modules.
But another catch here is that you will also be needing to capture the progress
of course_exersises a particular user has completed so that after completing
all the exercises you can update the course_module.
Note: Entery in below mentioned table in done only when a user has completed the
given exercise, also we will be having the timestamp as provided by rails.
User
has_many :courses, through: :user_courses
has_many :exercises, through: :user_course_exercise
UserCourseExercise
belongs_to :user
belongs_to :course_exercise
#table columns
user_id
exercise_id
Entry in this table will be done if all the exercises of a particular course has
been completed.
UserCourse
belongs_to :user
belongs_to :course_exercise
#table columns
user_id
course_id
The approach of having two tables would be that when you need to show the exercise
data corresponing to a particular user then you will be using user_course_exercise
and when completed courses are needed then usign the user_course table

ActiveModel::MissingAttributeError (can't write unknown attribute `user_id`)

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.

crossing borders of tables: language role

I'm new to development, and have spent the last 12 hours (literally) trying to figure out this error message - I'm giving up for the night, but not before a quick cry for help to stackoverflow.
I have this form:
<h2>Select from the language options below (or, <%= button_to "Login", 'users/login', method: :get %></h2>
<%= form_for #language_role do |f| %>
<div id="input">
<h3>I want to learn:</h3><%= select_tag(:language_id, options_from_collection_for_select(Language.all, :id, :lang_name)) %>
</div>
<div>
<p><%= f.submit "Start learning" %></p>
</div>
<% end %>
which is giving me this error message, highlighting the line #language_role = current_user.language_roles.build : "undefined method `language_roles' for nil:NilClass"
I have three tables:
create_table "language_roles", force: :cascade do |t|
t.integer "language_id"
t.boolean "is_active"
t.boolean "is_teacher"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.index ["user_id"], name: "index_language_roles_on_user_id"
end
create_table "languages", force: :cascade do |t|
t.string "lang_name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
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
The language_roles table is meant to allow a user to have many languages, as well as many roles within that language. Here are my class definitions:
class LanguageRole < ApplicationRecord
belongs_to :languages
belongs_to :users
end
class Language < ApplicationRecord
has_many :language_roles
has_many :users, :through => :language_roles
end
class User < ApplicationRecord
has_many :language_roles
has_many :languages, :through => :language_roles
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
My root path goes to 'home#index', where the user is supposed to pick a language if current_user.language_roles is empty. As such, I put this code in my home controller and language_roles controller:
class HomeController < ApplicationController
def index
#language_role = current_user.language_roles.build
end
end
class LanguageRolesController < ApplicationController
def create
#language_role = current_user.language_roles.build(language_role_params)
if #language_role.save
redirect_to root_path
else
redirect_to :back
end
end
private
def language_role_params
params.permit(:language_id)
end
end
What in the hell is the problem?? I assume I need to instantiate the variable somehow, but I'm not sure how.
Thanks,
Michael
There is a typo in your LanguageRole Model:
LanguageRole < ApplicationRecord
belongs_to :languages
belongs_to :users
end
should be
LanguageRole < ApplicationRecord
belongs_to :language
belongs_to :user
end
belongs_to associations must use the singular term.
The name of the other model is pluralized when declaring a has_many association.
Ref: http://guides.rubyonrails.org/association_basics.html
Your current_user is not defines it seems. You can install a gem called 'pry-rails' and debug your way out of this situation and any other in future. Here's a tutorial how to use it Railscasts #280
In your LanguageRole model you defined like belongs_to :users. But it should be belongs_to :user.
Your model look like ...
LanguageRole < ApplicationRecord
belongs_to :languages
belongs_to :users
end
Which should be something like
LanguageRole < ApplicationRecord
belongs_to :language
belongs_to :user
end

How does Rails know the primary key exists in this association according to my schema?

So I'm a little confused:
From my understanding the table on the receiving end of the association (belongs_to) has the foreign key, and that is what connects that table to the table with the primary key.
In the following schema though, the primary is never specified (user_id) in the 'users' table. Only the foreign key(user_id) on the 'pics' table is.
So how does Rails know the primary key exists in the first place, if it's never even specified?
(I am using Devise)
ActiveRecord::Schema.define(version: 20161220075312) do
create_table "pics", force: :cascade do |t|
t.string "title"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
add_index "pics", ["user_id"], name: "index_pics_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
These are my models:
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 :pics
end
Pic.rb
class Pic < ActiveRecord::Base
belongs_to :user
end
Rails automatically adds a primary key called id by default to all tables, so the table users has an 'id' primary key.
In the docs:
http://guides.rubyonrails.org/active_record_migrations.html
A primary key column called id will also be added implicitly, as
it's the default primary key for all Active Record models
To add to answers of Joel and Stefan, this is called "convention over configuration" and it is a core strength of rails.
So even though there aren't any foreign keys (the db objects) in your schema, rails sees has_many :pics and it assumes there must be a table called "pics" with a column named "user_id". And values of that column must correspond with values of column 'id' in table 'users'.
If you do things the rails way, you can write this configuration-free code. But if you, say, wrap a legacy database, where naming does not match rails' expectations, you can override the names.
has_many :pics, foreign_key: 'uid'

Resources