I'm creating a license server, but I stuck with problem, that Rails can't save model.
I set after_create method in User model, but got no luck, also I tried create License model with Rails console, but it rollback transaction and didn't show any error.
models/user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable,
authentication_keys: [:login]
attr_writer :login
has_one :license, dependent: :destroy
validates :username, presence: true, uniqueness: { case_sensitive: false }
validates_format_of :username, with: /^[a-zA-Z0-9_\.]*$/, multiline: true
after_create :create_assocs
def login
#login || self.username
end
def self.find_first_by_auth_conditions(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
where(conditions).where(['lower(username) = :value OR lower(email) = :value', { value: login.downcase }]).first
else
if conditions[:username].nil?
where(conditions).first
else
where(username: conditions[:username]).first
end
end
end
def email_required?
false
end
private
def create_assocs
create_license(license_types_id: LicenseType.first.id)
# license = License.new(user_id: self.id, license_types_id: 1)
# license.save
# self.license.create(license_types_id: LicenseType.first.id)
end
end
models/license.rb
class License < ApplicationRecord
belongs_to :license_type
belongs_to :user
after_create :set_expired_at
private
def set_expired_at
# self.expired_at = DateTime.now + self.license_types.duration
end
end
in rails console,
2.5.1 :001 > license = License.new(license_types_id: LicenseType.first.id)
LicenseType Load (0.4ms) SELECT "license_types".* FROM "license_types" ORDER BY "license_types"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<License id: nil, expired_at: nil, created_at: nil, updated_at: nil, license_types_id: 1, user_id: nil>
2.5.1 :002 > license.save
(0.5ms) BEGIN
(0.2ms) ROLLBACK
=> false
schema.rb,
create_table "licenses", force: :cascade do |t|
t.datetime "expired_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "license_types_id"
t.bigint "user_id"
t.index ["license_types_id"], name: "index_licenses_on_license_types_id"
t.index ["user_id"], name: "index_licenses_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: ""
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 "username"
t.string "key"
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 "licenses", "users"
What should I do to set the license for new user after creating?
License model contains two foreign key user_id and license_type_id
=> Which means before create License there must be a user who own this License as Rails 5 convention says user_id must exist
=> Also there must exist LicenseType as Rails 5 convention says license_type_id must exist
The rollback reasons can be investigated by following
license = License.new(license_types_id: LicenseType.first.id)
license.save
#Begin
#ROLLBACK
errors_stack = license.errors
errors_stack contains model level errors which causes rollback
To fix these rollback issue
user = User.first #or current_user
license = user.license.new(license_type_id: LicenseType.first.id)
license.save
Or
user = User.first #or current_user
license = License.new(license_type_id: LicenseType.first.id, user_id: user.id)
license.save
Or To create a User and assign the user a License # Alternative of after_create :create_assocs
new_user = User.new(...)
new_user.license.build(license_type_id: LicenseType.first.id)
new_user.save
Are you sure about this "no errors"? In Rails 5, belongs_to association is required by default, so I guess that's because it fails (you don't set user association prior to save attempt). So either you should set license.user, or set:
belongs_to :user, optional: true
in License model if your business logic doesn't require it.
Related
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.
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
I'm having an error on signup. Console indicated its an error "User exists" however i think its a problem with acts_as_paranoid conflicting with devise gem. Can you please help?
Gem - Device & ActsAsParanoid
It was working fine until i added acts_as_paranoid gem
Console response:
Started POST "/users" for 127.0.0.1 at 2018-09-18 20:25:17 +1000
Processing by Users::RegistrationsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"WBPVa4QVwzij/j1H+6uOMNURddc2CQX/YJJ+pIKXi3mRwa4aIgOcYbwQKsPGO5sjFYUlC89lH1mn7SpmkYZ1qw==", "user"=>{"first_name"=>"Ben", "last_name"=>"Strachan", "email"=>"ben#ownerhealth.com.au", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}
(0.2ms) BEGIN
User Exists (0.8ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 AND "users"."deleted_at" IS NULL LIMIT $2 [["email", "ben#ownerhealth.com.au"], ["LIMIT", 1]]
(0.2ms) ROLLBACK
Rendering devise/registrations/new.html.erb within layouts/auth
Rendered devise/shared/_links.html.erb (0.6ms)
Rendered devise/registrations/new.html.erb within layouts/auth (7.5ms)
(0.3ms) BEGIN
User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE "users"."email" = $1 AND "users"."deleted_at" IS NULL LIMIT $2 [["email", "ben#ownerhealth.com.au"], ["LIMIT", 1]]
(0.2ms) ROLLBACK
Completed 200 OK in 198ms (Views: 32.5ms | ActiveRecord: 2.2ms)
User model:
# == Schema Information
#
# Table name: users
#
# id :bigint(8) not null, primary key
# email :string default(""), not null
# encrypted_password :string default(""), not null
# reset_password_token :string
# reset_password_sent_at :datetime
# remember_created_at :datetime
# sign_in_count :integer default(0), not null
# current_sign_in_at :datetime
# last_sign_in_at :datetime
# current_sign_in_ip :string
# last_sign_in_ip :string
# created_at :datetime not null
# updated_at :datetime not null
# first_name :string
# last_name :string
# role :string
# invitation_token :string
# invitation_created_at :datetime
# invitation_sent_at :datetime
# invitation_accepted_at :datetime
# invitation_limit :integer
# invited_by_type :string
# invited_by_id :integer
# invitations_count :integer default(0)
# avatar_file_name :string
# avatar_content_type :string
# avatar_file_size :integer
# avatar_updated_at :datetime
# business_id :integer
# author_id :integer
# deleted_at :datetime
#
class User < ApplicationRecord
acts_as_paranoid
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
ROLES = [
ROLE_ADMIN = "Admin",
ROLE_REGULAR = "Regular"
]
devise :invitable, :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
validates :first_name, presence: true, length: { maximum: 50 }
validates :last_name, presence: true, length: { maximum: 50 }
validates :role, inclusion: ROLES, presence: true
has_attached_file :avatar, styles: { medium: "450x450>" },
default_url: -> (attachment) {
ActionController::Base.helpers.asset_path(
'default-avatar.png'
)
}
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
before_validation :set_role
belongs_to :business, optional: true
def full_name
[first_name, last_name].join(" ")
end
def admin?
self.role == ROLE_ADMIN
end
private
def set_role
self.role = ROLE_REGULAR if self.role.blank?
end
end
Registration controller:
class Users::RegistrationsController < Devise::RegistrationsController
layout 'auth'
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
# GET /resource/sign_up
# def new
# super
# end
# POST /resource
def create
super
if resource.save
business = Business.create first_name: resource.first_name,
last_name: resource.first_name,
email: resource.email
resource.update business_id: business.id
end
end
Schema:
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.string "first_name"
t.string "last_name"
t.string "role"
t.string "invitation_token"
t.datetime "invitation_created_at"
t.datetime "invitation_sent_at"
t.datetime "invitation_accepted_at"
t.integer "invitation_limit"
t.string "invited_by_type"
t.integer "invited_by_id"
t.integer "invitations_count", default: 0
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.integer "business_id"
t.integer "author_id"
t.datetime "deleted_at"
t.index ["deleted_at"], name: "index_users_on_deleted_at"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
t.index ["invitations_count"], name: "index_users_on_invitations_count"
t.index ["invited_by_id"], name: "index_users_on_invited_by_id"
t.index ["invited_by_type", "invited_by_id"], name: "index_users_on_invited_by_type_and_invited_by_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
I made an error! The user author_id relationship was wrong. This has fixed.
belongs_to :author, class_name: 'User', foreign_key: 'author_id', optional: true
I am trying to create a view for photo uploads for users. I'm new to rails so I'm not quite sure if I am doing this correctly because I'm not quite sure how all the pieces fit. I am using devise and also carrierwave for user authentication and image storage in database. I'm not quite sure what to do with the params for IncomePicture_params. I want to create a view that will allow me to call and display the images and the texts for pictures of the user
I am using rails 4
Models:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:rememberable, :validatable
validates_presence_of :username
has_many :expense_pictures
has_many :income_pictures
end
class IncomePicture < ActiveRecord::Base
belongs_to :user
mount_uploader :image, ImageUploader
has_one :income_text
accepts_nested_attributes_for :income_text
end
class IncomeText < ActiveRecord::Base
belongs_to :income_picture
end
controller:
class UserController < ApplicationController
def create
User.create(user_params)
end
private
def user_params
# required input for params
# permit - returns a version of the params hash with ony the permitted attributes
params.require(:user).permit(:name, :email, :password, :password_confirmation, )
end
end
class IncomePicturesController < ApplicationController
def new
#income_picture = IncomePicture.new(IncomePicture_params)
end
def create
end
def destroy
end
private
def IncomePicture_params
params.require(:income_picture).permit(:image, income_text_attributes: [:amount])
end
end
schema
ActiveRecord::Schema.define(version: 20140723044409) do
create_table "income_pictures", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.string "image"
t.integer "user_id"
end
add_index "income_pictures", ["user_id"], name: "index_income_pictures_on_user_id"
create_table "income_texts", force: true do |t|
t.datetime "created_at"
t.datetime "updated_at"
t.integer "income_picture_id"
t.string "amount"
end
add_index "income_texts", ["income_picture_id"], name: "index_income_texts_on_income_picture_id"
create_table "users", force: true do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.datetime "remember_created_at"
t.datetime "created_at"
t.datetime "updated_at"
t.string "username"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
Here's my User 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 :personas
has_many :stories, through: :persona
validates_presence_of :name
attr_accessor :name, :default_persona_id
after_create :create_first_persona
private
def create_first_persona
#persona = Persona.new
#persona.user = self
#persona.name = self.name
if #persona.save
make_first_persona_default
end
end
def make_first_persona_default
#user = self
#user.default_persona_id = #user.personas.first.id
#user.save!(:validate => false)
end
end
What it does is create a Persona each time a User signs up and then sets that Persona's id as the User's default_persona_id.
Everything works except make_first_persona_default. When I check the user in rails console default_persona_id is nil.
I'm on Rails 4.
UPDATE
Edited make_first_persona_default to Taryn East's
def make_first_persona_default
unless self.update_attribute(:default_persona_id, self.personas.first.id)
raise "got an error trying to save persona: #{self.errors.inspect}"
end
end
default_persona_id is still nil
User Load (1.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 13, email: "[FILTERED]", encrypted_password: "[FILTERED]", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2013-10-01 02:09:19", last_sign_in_at: "2013-10-01 02:09:19", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", created_at: "2013-10-01 02:09:19", updated_at: "2013-10-01 02:09:19", default_persona_id: nil>
Here's my schema for User.
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.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "default_persona_id"
end
and the migration which I used to add the default_persona_id column.
class AddActivePersonaToUsers < ActiveRecord::Migration
def change
change_table :users do |t|
t.integer :default_persona_id
end
end
end
I wonder what this line is doing in your User model:
attr_accessor :name, :default_persona_id
Did you mean accessible? Creating accessor this way will override ActiveRecord accessor, so assigning default_persona_id will only set instance variable #default_persona_id and will have no effect on database.
In reality - there's no reason why we have to lose the context.
Also - rather than creating one and then adding myself as a related user - you can create the persona directly on the association, and it will automatically link itself.
I'd do it this way:
private
def create_first_persona
persona = self.personas.build(:name => self.name)
if persona.save!
self.update_attribute(:default_persona_id, persona.id)
end
end
If you wish to continue with the two methods manner, the build will help with that.
I suspect the problem in your original code is that you are not building on the association - and thus the
"personas" needs a reload before the user can find the new persona.
Also you don't need to fetch out the current user the way you do.. you already have self, so just use self. eg:
def make_first_persona_default
self.default_persona_id = self.personas.first.id
self.save!(:validate => false)
end
or even better, you're only setting one attribute... so use update attribute
def make_first_persona_default
self.update_attribute(:default_persona_id, self.personas.first.id)
end