save method not writing to the database - ruby-on-rails

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

Related

Rails rollback while saving model

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.

undefined method `firstname' for nil:NilClass Rails4

Can somebody help me with this error? I add profile controller by console without model. Database it's working fine. I am sure that i have lastname and firstname in seed.rb and i just did db:setup/migration.
Here's the show.html
.page-header
.h1
=link_to #user.firstname + " " + #user.lastname, edit_user_registration_path
Database:
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.string "firstname"
t.string "lastname"
t.string "username"
t.boolean "admin", default: false
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 :reviews
has_many :products
validates_presence_of :firstname, :lastname
end
Profile controler:
class ProfileController < ApplicationController
def show
#user = User.find_by_username(params[:id])
if #user
#reviews=#user.reviews.order(:created_at).reverse_order.limit(5)
render action: :show
end
end
end
This part
#user = User.find_by_username(params[:id])
returns nil, because you pass id from params as a username to find_by_username finder. In can't find the username (string) which is id (integer).
It should be
#user = User.find(params[:id])
Take a look at docs on how finders work
If you are using find_by_username then you should pass params[:username] instead of params[:id] .
Also this part:
if #user
#reviews=#user.reviews.order(:created_at).reverse_order.limit(5)
render action: :show
end
even if the if statement is false, render action: :show will still be called - it's the default.
if you defined a method that said nothing:
def test
end
rails would call: render action: :name_of_method
You need to use a redirect and unless:
redirect_to users_url, notice: 'user not found' unless #user
Good luck!

Devise returns nil after User.first

I'm testing some associations after adding a new migration that associates users to many posts.
class DeviseCreateUsers < ActiveRecord::Migration
def change
create_table(:users) do |t|
## Customization
t.string :name
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
## Recoverable
t.string :reset_password_token
t.datetime :reset_password_sent_at
## Rememberable
t.datetime :remember_created_at
## Trackable
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
## Confirmable
t.string :confirmation_token
t.datetime :confirmed_at
t.datetime :confirmation_sent_at
t.string :unconfirmed_email # Only if using reconfirmable
## Lockable
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
# t.string :unlock_token # Only if unlock strategy is :email or :both
# t.datetime :locked_at
t.timestamps
end
add_index :users, :email, unique: true
add_index :users, :reset_password_token, unique: true
# add_index :users, :confirmation_token, unique: true
# add_index :users, :unlock_token, unique: true
end
end
After I migrated, I'm supposed to test with
u = User.first
to receive this block of code make sure everything is running smoothly.
User Load (0.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 1, name: nil, email: "username#example.com", encrypted_password: "$2a$10$702fd8Io3WH7UTWoTY3rUeUJBcFVlsq8/K6ypPKZUQni...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 0, current_sign_in_at: nil, last_sign_in_at: nil, current_sign_in_ip: nil, last_sign_in_ip: nil, confirmation_token: nil, confirmed_at: "2013-04-29 20:25:26", confirmation_sent_at: "2013-04-29 20:25:09", unconfirmed_email: nil, created_at: "2013-04-29 20:25:09", updated_at: "2013-04-29 20:25:26">
but I got nil
2.1.5 :001 > u = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> nil
Is this a bad thing? Why isn't anything showing up?
Your database is empty at present. And hence you are getting a nil. You can do this:
1) Add a new user to the table using devise sign-up functionality in the front end.
2) Add seed data in the db/seeds.rb file like this:
User.find_or_create_by(:email => 'tester#test.com', :password => 'password', :password_confirmation => 'password')
Run rake db:seed from command prompt. Seeds data is the initial data that you want to add to your database for the application to run as desired.
3) Use Rails console: Run rails c in your command prompt and do this:
User.create(:email => 'tester#test.com', :password => 'password', :password_confirmation => 'password')
You need to do something like this in your console to add the first user:
user = User.new(:email => 'tester#test.com', :password => 'testingpassword',
:password_confirmation => 'testingpassword')
user.save
Devise doesn't create a default user for you, so until you create one, user.first wil come back as nil

Pass Account id (parent) to User (child) with Devise in Rails

I have set up the necessary models and views to have a Devise resource, Account and User. Account has_many users & User belongs_to account. The schema reflects account_id as an attribute of user. I'm probably mistaken but I was under the impression that this account_id attribute would automatically be filled when an Account is logged in & creates a new user. Upon checking the Rails console, it seems all new users created this way had a nil value for account_id. Separate question but is this the ideal way to have multitenancy in an application using Devise?
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
belongs_to :account, :inverse_of => :users
accepts_nested_attributes_for :account
end
account.rb
class Account < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :users, :inverse_of => :account, :dependent => :destroy
accepts_nested_attributes_for :users
has_many :projects
end
schema.rb (just users & accounts)
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 "account_id"
t.boolean "supervisor"
t.string "name"
end
create_table "accounts", 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.string "name"
end
users_controller.rb
class UsersController < ApplicationController
before_filter :authenticate_user!
def new
#user = User.new
end
def create
#user.skip_confirmation! # confirm immediately--don't require email confirmation
if #user.save
flash[:success] = "User added and activated."
redirect_to users_path # list of all users
else
render 'new'
end
end
def index
#users = User.all
end
end
accounts_controller.rb
class AccountsController < ApplicationController
def new
#accounts = Account.new
#accounts.users.build
end
def create
#account = Account.new(params[:account])
if #account.save
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
end
from registrations > edit.html.erb
<% if account_signed_in? %>
<div class="jumbotron">
<span><%= link_to "Add user", new_user_registration_path, class: "btn btn-primary btn-sm" %></span>
</div>
<% end %>
I am not sure what how exactly you are trying to create a user
But creating a user with
#account.users.build
would automatically add account_id to user object.
Hope this helps! :)

Omniauth error -- PG::Error: ERROR: duplicate key value violates

So, I've helped a friend add Omniauth to his app, and for some reason omniauth only worked the first time.
But now, it's returning:
PG::Error: ERROR: duplicate key value violates
unique constraint "index_users_on_email"
DETAIL: Key (email)=() already exists. : INSERT INTO "users"
("created_at", "provider", "uid", "updated_at", "username")
VALUES ($1, $2, $3, $4, $5) RETURNING "id"
It's like it only accepts one, and then says "already exists". How can we get this workin'?
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :token_authenticatable, :confirmable,
# :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable
has_many :songs
has_many :comments
acts_as_voter
def self.from_omniauth(auth)
where(auth.slice(:provider, :uid)).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.username = auth.info.nickname
end
end
def self.new_with_session(params, session)
if session["devise.user_attributes"]
new session["devise.user_attributes"] do |user|
user.attributes = params
user.valid?
end
else
super
end
end
def password_required?
super && provider.blank?
end
def update_with_password(params, *options)
if encrypted_password.blank?
update_attributes(params, *options)
else
super
end
end
def email_required?
super && provider.blank?
end
end
schema snippit
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
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.boolean "admin"
t.string "provider"
t.string "uid"
t.string "username"
end
omni controller
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def all
user = User.from_omniauth(request.env["omniauth.auth"])
if user.persisted?
flash.notice = "Signed in!"
sign_in_and_redirect user
else
session["devise.user_attributes"] = user.attributes
redirect_to new_user_registration_url
end
end
alias_method :twitter, :all
end
All access keys are setup appropriately following railscasts latest ep on it.
You have a unique constraint on the email field. Because this field is defined as not nullable and has a default value of "" (empty string). This means if you don't specify the email, your DBMS will set this field to "". Of course, because it has a unique constraint, you'll only be able to add two users without email, because only one can have the empty string email.
If you didn't create the constraint manually, then I assume rails has created it for you (because there's an index on the email field too apparently) by assuming the first field is the table primary key, since you didn't specify any. This made it create both an index and a unique constraint on it.
A more simple explanation could be that this is caused due to your test data being generated without a unique email. Try looking in /test/fixtures/users.yml, if you see something like:
one: {
}
two: {
}
then change it to
one: {
email: tester1#stack.com
}
one: {
email: tester2#stack.com
}
Or you can delete the contents of the file.
You can remove the index from the email since devise sets it as unique. You can't have duplicate blank email addresses according to how devise sets up things by default. Here is a migration example.
class ChangeEmail < ActiveRecord::Migration
def change
remove_index :users, :email
end
end

Resources