Custom email unique validation not working on devise - ruby-on-rails

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.

Related

Rails help - unknown attribute 'creator_id' for Event

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

User create a team or join a team after sign up / log in

I'm currently working on a Ruby on Rails application where a devise user when successfully logged in / signed up can create a team or join a team. The user who creates the team will become the owner and can't belong to any other teams, a user that decides to join a team will see all teams that have been made and are able to join them. I'm having a lot of trouble understanding the relations I need to use for this
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
validates_presence_of :phone, :city, :state, :street, :zip, presence: true, on: :create
has_one :team
end
schema.rb
create_table "teams", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "team_name"
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", null: false
t.datetime "updated_at", null: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "firstname"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
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
team.rb
class Team < ApplicationRecord
belongs_to :user, class_name: 'User'
end
routes.rb
devise_for :users, path: 'users' , controllers: { sessions: "users/sessions", confirmations: 'users/confirmations', registrations: 'users/registrations' }
Am I using the right approach?

Ruby on rails associations - User (devise) can 'create a team' or 'join team' after sign up and log in

Hey guys im working on a rails application where users that have successfully signed up and logged in to their dashboard can create a team and become the leader (invite others to join via email, delete existing members etc.) or join an existing team as a member.( can only view other members and information about themselves) I'm using the Devise gem for user login / sign up since it has a lot of what i need. A team has one manager and has many members. Heres what ive tried, users belong to teams, and teams have many users. Ive also tried the association, Users have one team and teams have many users but one manager. I'm very confused thanks again.
user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable, :confirmable
validates_presence_of :phone, :city, :state, :street, :zip, presence: true, on: :create
has_one :team
end
schema.rb
create_table "teams", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "team_name"
t.string "team_statement"
t.bigint "user_id"
t.index ["user_id"], name: "index_teams_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.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "firstname"
t.string "middlename"
t.string "lastname"
t.string "phone"
t.string "city"
t.string "state"
t.string "street"
t.string "zip"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
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
team.rb
class Team < ApplicationRecord
belongs_to :user, class_name: 'User'
end
routes.rb
devise_for :users, path: 'users' , controllers: { sessions: "users/sessions", confirmations: 'users/confirmations', registrations: 'users/registrations' }
You just add integer field leader_id to teams table. Also, you should add this row to team.rb:
belongs_to :leader, class_name: 'User'
that helps you find a leader from a team (like team.leader).
You should not use has_and_belongs_to_many association.

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

Issue with belongs_to and user (devise)

I'm new to RoR, and I would like to develop an app, but I have an issue with the belongs_to association. I am using devise for the authentication of my users, and I have an object called timesheet, I followed several tutorials and read a lot of forums but unfortunately user_id remains null in my db, so I do not know where does the problem come from.
If you can tell how to fix it, any links that can helps me, that would be great.
Schema.rb:
ActiveRecord::Schema.define(version: 20150128160116) do
create_table "timesheets", force: true do |t|
t.date "date"
t.time "working_start_time"
t.time "working_end_time"
t.integer "breaks"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "timesheets", ["user_id"], name: "index_timesheets_on_user_id"
create_table "users", force: true 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.integer "current_sign_in_ip"
t.integer "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
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
timesheets_controller.rb
class TimesheetsController < ApplicationController
layout 'application'
def show
#timesheet=Timesheet.find(params[:id])
end
def index
#timesheet = Timesheet.all
end
def new
#timesheet = Timesheet.new
end
def create
#timesheet = Timesheet.create(timesheet_params)
redirect_to new_timesheet_path
end
def edit
#timesheet=Timesheet.find(params[:id])
end
def update
#timesheet = Timesheet.find(params[:id])
#timesheet.update_attributes(timesheet_params)
redirect_to student_table_path
end
user.rb 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 :timesheets
end
Timesheet.rb model
class Timesheet < ActiveRecord::Base
belongs_to :user
validates :user_id, :presence => true
end
Thanks in advance.
It will stay null because you are not using it in the timesheetsController, your create action should be like this :
def create
#timesheet = current_user.timesheets.build(timesheet_params)
redirect_to new_timesheet_path
end
You have to use that build method to reference the current_user, so the timesheet will have the current_user in the user_id field.

Resources