Inverse record of a belongs_to record from a registration form not saving attributes - devise

I have a registration form that asks for four things: the user's email, the user's name, their company's name, and a password.
The user model contains the email, name, and password fields
class User < ApplicationRecord
belongs_to :account
attr_accessor :account_attributes
accepts_nested_attributes_for :account
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
The account model contains the company's name.
class Account < ApplicationRecord
has_many :users,
:dependent => :destroy
attr_accessor :name
validates :name, presence: true
end
My registration form looks like (using slim templates):
h2
| Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= devise_error_messages!
.field
= f.label :email
br
= f.email_field :email, autofocus: true
.field
= f.label :name, "Your name"
br
= f.text_field :name
= f.fields_for :account_attributes do |a|
.field
= a.label :name, "Your company or organization"
br
= a.text_field :name
.field
= f.label :password
- if #minimum_password_length
em
| (
= #minimum_password_length
| characters minimum)
br
= f.password_field :password, autocomplete: "off"
.field
= f.label :password_confirmation
br
= f.password_field :password_confirmation, autocomplete: "off"
.actions
= f.submit "Sign up"
= render "devise/shared/links"
And lastly, my custom registration controller is:
class RegistrationsController < Devise::RegistrationsController
def new
super
resource.build_account
end
def create
super
resource.account ||= Account.new
resource.account.name = sign_up_params[:account_attributes]["name"]
resource.account.save
end
private
def sign_up_params
params.require(:user).permit(:first_name, :name, :email, :password, :password_confirmation, :account_attributes => [:name])
end
end
The account relation itself persists, but the account NAME does not persist. Looking at the server logs, I can see where the account is persisted but I do not see the name persisting. The strange thing is I can see in the server where it attempts to do the transaction but ultimately doesn't issue any SQL update statement (and doesn't issue a rollback indicating there is no validation error).
SQL (0.2ms) INSERT INTO "accounts" ("created_at", "updated_at") VALUES (?, ?) [["created_at", 2016-10-27 16:39:33 UTC], ["updated_at", 2016-10-27 16:39:33 UTC]]
SQL (0.2ms) INSERT INTO "users" ("name", "account_id", "created_at", "updated_at", "email", "encrypted_password") VALUES (?, ?, ?, ?, ?, ?) [["name", "dsfdsfds"], ["account_id", 32], ["created_at", 2016-10-27 16:39:33 UTC], ["updated_at", 2016-10-27 16:39:33 UTC], ["email", "dsfsdfsdf#fdsfasdfdsfsdfdsc.com"], ["encrypted_password", "$2a$11$pUhS5LGJO2VjPvlPVwj0KO6Ce5Ysr8s4Cu.R4kmsWe7CEayk7t8Fm"]]
(2.2ms) commit transaction
(0.0ms) begin transaction
(0.0ms) commit transaction
(0.0ms) begin transaction
SQL (0.3ms) UPDATE "users" SET "updated_at" = ?, "sign_in_count" = ?, "current_sign_in_at" = ?, "last_sign_in_at" = ?, "current_sign_in_ip" = ?, "last_sign_in_ip" = ? WHERE "users"."id" = ? [["updated_at", 2016-10-27 16:39:33 UTC], ["sign_in_count", 1], ["current_sign_in_at", 2016-10-27 16:39:33 UTC], ["last_sign_in_at", 2016-10-27 16:39:33 UTC], ["current_sign_in_ip", "::1"], ["last_sign_in_ip", "::1"], ["id", 36]]
(0.5ms) commit transaction
Redirected to http://localhost:3000/products
I'm tearing my hair out and I've researched every SO question I could find that's seemingly related. I can do puts sign_up_params[:account_attributes]["name"] in the controller and the name I enter into the form prints back properly, I just can't seem to assign it to the model.

The attr_accessor on the account model is what caused this. By defining an attr_accessor, I was overriding Rails built in attribute accessors for database columns.
Simply removing attr_accessor :name from the Account model fixed this.

Related

Saving wrong id for messaging system

I am trying to set up a messaging system on my app and I had it working, but now ActiveRecord is saving the wrong id for the recipient_id.
Here's where the user.id is loaded as 3 but when the values are inserted the messages's recipient.id is 5:
User Load (0.6ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 ORDER BY "users"."id"
ASC LIMIT 1
Message Load (0.4ms) SELECT "messages".* FROM "messages" LIMIT 1
User Load (0.6ms) SELECT "users".* FROM "users" LIMIT 1
(0.5ms) BEGIN
SQL (1.3ms) INSERT INTO "messages" ("content", "created_at", "recepient_id", "sender_id",
"subject", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["content", "erferfe"],
["created_at", Wed, 17 Dec 2014 19:15:08 UTC +00:00], ["recepient_id", 5], ["sender_id", 3],
["subject", "erfer"], ["updated_at", Wed, 17 Dec 2014 19:15:08 UTC +00:00]]
(5.4ms) COMMIT
Redirected to http://0.0.0.0:3000/messages
Completed 302 Found in 23ms (ActiveRecord: 9.6ms)
Here's MessagesController#create:
def create
#message = current_user.sent_messages.new(message_params)
#message.recepient_id = User.find_by(params[:id]).id
if #message.save
flash[:notice] = 'Message has been sent.'
redirect_to messages_path
else
render :action => :new
end
end
Here's messages.rb:
class Message < ActiveRecord::Base
belongs_to :sender, class_name: "User", primary_key: "sender_id"
belongs_to :recepient, class_name: "User", primary_key: "recepient_id"
belongs_to :user
This is the routing for the messages:
resources :users do
resources :messages
end
This is the view:
<div class="ui button"><i class="mail icon"></i><%= link_to 'Message', new_user_message_path(#user) %></div>
Message_params:
def message_params
params.require(:message).permit(:subject, :user_id, :content, :recepient_id)
end
User.rb
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, :omniauthable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauth_providers => [:facebook, :twitter]
TEMP_EMAIL_PREFIX = 'change#me'
TEMP_EMAIL_REGEX = /\Achange#me/
attr_accessor :login
has_many :projects, class_name: "Project", foreign_key: "creator_id"
has_many :sent_messages, class_name: "Message", foreign_key: "sender_id"
has_many :recieved_messages, class_name: "Message", foreign_key: 'recepient_id'
has_many :messages
has_many :projects, dependent: :destroy
has_many :authentications, :dependent => :destroy
validates :email, presence: true,
uniqueness: true,
format: {
with: /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9\.-]+\.[A-Za-z]+\Z/
}
validates_format_of :email, :without => TEMP_EMAIL_REGEX, on: :update
def downcase_email
self.email = email.downcase
end
def generate_password_reset_token
update_attribute(:password_reset_token, SecureRandom.urlsafe_base64(48))
end
def self.find_for_facebook_oauth(auth)
user = User.where(:provider => auth.provider, :uid => auth.uid).first
unless user
user = User.create(:first_name => auth.extra.raw_info.first_name,
:last_name => auth.extra.raw_info.last_name,
:avatar => auth.info.image,
:provider => auth.provider,
:uid => auth.uid,
:email => auth.info.email,
:password => Devise.friendly_token[0,20]
)
user.confirm!
end
user.save
end
def self.find_for_twitter_oauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.name = auth.info.name
user.avatar = auth.info.image
user.save
end
end
After code change suggested:
User Load (1.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = 2 ORDER BY "users"."id" ASC
LIMIT 1
Message Load (0.3ms) SELECT "messages".* FROM "messages" LIMIT 1
(0.1ms) BEGIN
SQL (0.4ms) INSERT INTO "messages" ("content", "created_at", "sender_id", "subject", "updated_at")
VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["content", "efvefvf"], ["created_at", Wed, 17 Dec 2014
21:02:48 UTC +00:00], ["sender_id", 2], ["subject", "erfvef"], ["updated_at", Wed, 17 Dec 2014
21:02:48 UTC +00:00]]
(7.2ms) COMMIT
Also when I go /users/:user_id/messages my css and javascript files don't work
I am assuming that this feature is initialized from a User "Profile" page which allows the current session user (sender) to send a message to the profile user (recipient).
First off, in your Message model, you do not need belongs_to :user on the Message model nor do you need has_many :messages on the User model.
Next, you have way over complicated the #create action:
def create
#message = current_user.sent_messages.new(message_params)
#message.recipient_id = params[:user_id]
if #message.save
flash[:notice] = 'Message has been sent.'
redirect_to messages_path
else
render :action => :new
end
end
Now, adjust your message_params. You do not need the user_id (will be provided by current_user) nor do you need the recipient_id (will be provided by the url param).
def message_params
params.require(:message).permit(:subject, :content)
end
Give that a try.

some fields not saved in sql table

Ruby 1.9.3p545 Rails 4.1.4
I am trying to save the fields from this form into a table Profile
<%= form_for #profile do |f| %>
<% if #profile.errors.any? %>
<div class="error_messages">
<h2>Form is invalid</h2>
<ul>
<% for message in #profile.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :password %><br />
<%= f.password_field :password %>
</p>
<p>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</p>
<p>
<%= f.label :interests %><br />
<%= autocomplete_field_tag 'interests', '', profile_autocomplete_interest_name_path %> </p>
<p>
<%= f.label :zip %><br />
<%= f.text_field :zip %>
</p>
<p>
<%= f.label :country %><br />
<%= f.country_select :country, ["United Kingdom"] %> </p>
<p class="button"><%= f.submit %></p>
The password and interests fields are not being saved.
The controller is
autocomplete :profile, :interest
def new
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
if #profile.save
The method in the Profile model is
attr_accessor :password, :encrypted_password, :password_confirmation
validates :name, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
validates :email, :presence => true, :uniqueness => true, confirmation: true
validates_email_realness_of :email
before_save :encrypt_password
logger.info "post save"
after_save :clear_password
def encrypt_password
if password.present?
password_salt = BCrypt::Engine.generate_salt
password= BCrypt::Engine.hash_secret(password, password_salt)
logger.info "debug passwords"
logger.debug password_salt
logger.debug password
end
end
def clear_password
self.password = nil
end
The table definition is
def change
create_table :profiles do |t|
t.string :name
t.string :email
t.string :interests
t.string :zip
t.string :country
t.string :password
t.boolean :author
t.date :dob
t.string :gender
t.boolean :optinemail
t.boolean :optinstories
t.timestamps
end
From the log
All parameters are passed successfully to profile model, and password built in encrypt_password as a result of
before_save :encrypt_password
but SQL statement has the two fields missing and nothing logged after that so after_save :clear_password not actioned
Started POST "/profiles" for 127.0.0.1 at 2014-07-29 12:05:40 +0100
Processing by ProfilesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"D8HVrGVeFudwz5O0rvf7orkW/TFJS3kXL6uv9lZOxEU=", "profile"=>{"name"=>"admin", "email"=>"info#good2use.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "zip"=>"b72 1af", "country"=>"United Kingdom"}, "interests"=>"golf", "commit"=>"Create Profile"}
[1m [35m (0.0ms) [0m begin transaction
[1m [36mProfile Exists (1.0ms) [0m [1mSELECT 1 AS one FROM "profiles" WHERE "profiles"."name" = 'admin' LIMIT 1 [0m
[1m [35mProfile Exists (0.0ms) [0m SELECT 1 AS one FROM "profiles" WHERE "profiles"."email" = 'info#good2use.com' LIMIT 1
debug passwords
$2a$10$GJDyoPHEd2/2zhEPRxaMvu
$2a$10$GJDyoPHEd2/2zhEPRxaMvu6v9j5mrOXt5klPgO0WNYvwGGqwrHH3m
[1m [36mSQL (1.0ms) [0m [1mINSERT INTO "profiles" ("country", "created_at", "email", "name", "updated_at", "zip") VALUES (?, ?, ?, ?, ?, ?) [0m [["country", "United Kingdom"], ["created_at", "2014-07-29 11:05:53.912000"], ["email", "info#good2use.com"], ["name", "admin"], ["updated_at", "2014-07-29 11:05:53.912000"], ["zip", "b72 1af"]]
[1m [35m (73.0ms) [0m commit transaction
Rendered layouts/application.html.erb within layouts/application (277.0ms)
If I try and manipulate any fields in table Profile from the rails console the data appears in the schema but whn saved SQL rolls the transaction back
This is driving the Profile model somehow as teh following log entry is cut
post save
[1m[36m (1.0ms)[0m [1mbegin transaction[0m
[1m[35mProfile Exists (0.0ms)[0m SELECT 1 AS one FROM "profiles" WHERE "profiles"."name" = 'admin1' LIMIT 1
[1m[36mProfile Exists (0.0ms)[0m [1mSELECT 1 AS one FROM "profiles" WHERE "profiles"."email" IS NULL LIMIT 1[0m
[1m[35m (0.0ms)[0m rollback transaction
That would seem to imply routing so here's the only part of routes that mention profile
get "sign_up" => "profiles#new", :as => "sign_up"
get 'profile/autocomplete_interest_name'
Hope soemone can help. It seems to be an SQL matter and it's driving me insane.
Thanks
Eddie Reader
Full model
class Profile < ActiveRecord::Base
attr_accessor :password, :encrypted_password, :password_confirmation
validates :name, :presence => true, :uniqueness => true, :length => { :in => 3..20 }
validates :password, :confirmation => true
validates_length_of :password, :in => 6..20, :on => :create
validates :email, :presence => true, :uniqueness => true
before_save :encrypt_password
logger.info "post save"
after_create :clear_password
def encrypt_password
if password.present?
password_salt = BCrypt::Engine.generate_salt
self.password= BCrypt::Engine.hash_secret(password, password_salt)
logger.info "debug passwords"
logger.debug password_salt
logger.debug password
end
end
def clear_password
self.password = nil
end
def self.authenticate(email, password)
user = find_by_email(:email)
password_salt = BCrypt::Engine.generate_salt
if user && user.password_hash == BCrypt::Engine.hash_secret(password, password_salt)
user
else
nil
end
end
def self.authenticate_by_email(email, password)
user = find_by_email(email)
password_salt = BCrypt::Engine.generate_salt
logger.info "debug salt variable"
logger.debug password_salt
logger.debug password
logger.debug BCrypt::Engine.hash_secret(password,password_salt)
logger.debug user
logger.debug user.password
logger.debug user.zip
if user && user.password == BCrypt::Engine.hash_secret(password,password_salt)
user
else
nil
end
end
def self.authenticate_by_username(username, password)
user = find_by_username(username)
password_salt = BCrypt::Engine.generate_salt
if user && user.password_hash == BCrypt::Engine.hash_secret(password, user.password_salt)
user
else
nil
end
end
end

Devise: custom fields not saved after signing up

Environment: Rails 4, Devise 3.2.2
Given I added two fields (name, profession) to my devise model.
When I sign up
Then I see in the database the new columns not affected.
Below are my controller and model classes
#custom controller
class UsersController < Devise::RegistrationsController
before_action :configure_permitted_parameters
#devise's controller extensions
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) do |u|
u.permit(:name, :email, :profession, :password, :password_confirmation)
end
end
end
#Devise Model
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
attr_accessor :name, :profession
validates :name, presence: true
validates_uniqueness_of :email, :name
end
I specify that devise runs correctly and uses my custom controller.
I also checked in the params hash and it includes the new field values as you can see:
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"F3AjMd1/EpYvuhckEMPUkiMZ1szHa6ba7OMbjjSltOk=", "user"=>{"name"=>"test", "email"=>"you#contect.com", "profession"=>"prof", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Save"}
And below is the generated sql:
INSERT INTO "users" ("created_at", "email", "encrypted_password", "updated_at") VALUES (?, ?, ?, ?) [["created_at", Tue, 11 Feb 2014 15:14:56 UTC +00:00], ["email", "you#contect.com"], ["encrypted_password", "$2a$10$Bn3LIldBUDPHKO0vrXd7peVz6q/42hOrCOcqdbvBMHVeEtn4EfKma"], ["updated_at", Tue, 11 Feb 2014 15:14:56 UTC +00:00]]
which contains none of new fields added.
Any hint will be welcome, I've been stuck for more than one hour.
Remove this line from your model:
attr_accessor :name, :profession
This is overriding the accessors already provided by activer record after you migrated your model and added those fields. This is causing them not to be saved to the db.

Rails 3 UnknownAttributeError For Nested Model

I've been getting the UnkownAttributeError for no particular reason, my models seem to be setup correctly...
School.rb
class School < ActiveRecord::Base
attr_protected :id, :created_at, :updated_at
#relationships
has_many :users
accepts_nested_attributes_for :users
end
My School model used to have the following, but it produced a MassAssignmentSecurity error for the user fields:
attr_accessible :country, :name, :state_or_province, :users_attributes
User.rb
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :instructor_id, :first_name, :last_name, :school_id
#relationships
belongs_to :school
end
new.html.haml
= simple_form_for #school do |f|
.well
= f.input :name, :as => :hidden
= f.input :country, :as => :hidden
= f.input :state_or_province, :as => :hidden
.well
= f.simple_fields_for #school.users.build do |user_form|
= user_form.input :first_name, :required => true
= user_form.input :last_name, :required => true
= user_form.input :username, :required => true
...
= f.button :submit, "Next"
Note: #school is being populated in my new action from session information gathered on the previous page, I'm making a multi-step form. The school data is perfectly valid, if I was to remove the user form it would have no trouble saving the school.
The specific error message I'm getting in my create action:
ActiveRecord::UnknownAttributeError in SchoolsController#create
unknown attribute: user
And the sent params looks a little like this:
{"school"=>{"name"=>"Elmwood Elementary", "country"=>"38",
"state_or_province"=>"448", "user"=>{"first_name"=>"joe",
"last_name"=>"asdas", "username"=>"asasdads",
"email"=>"asdasd#sdas.ca", "password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]"}}, "commit"=>"Next"}
Is this maybe a bug with either Devise or simple_form? I'm using Rails 3.2.3
Ok, so apparently I needed to provide the symbol :users - the name of the relationship as my first argument for it to work.

Problem with join table in rails

I have a join model that connects Authors to Products. It's called contracts. I want to create the contract as soon as the product is created, so in my product model I have:
after_save :create_contract
def create_contract
contract = Contract.new(
:product_id => self.id,
:author_id => #author_id
)
contract.save
end
It seemed simple enough to me, but :author_id always comes up nil when it's ready to go into the database. I've tried a couple different ways of setting it, nothing seems to work. I'm guessing it's related to how I'm submitting it with the products form, which looks like this:
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :handle %><br />
<%= f.text_field :handle %>
</div>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :keywords %><br />
<%= f.text_field :keywords %>
</div>
<div>
<%= collection_select( "contract", "author_id", #authors, "id", "full_name") %>
</div>
And in the controller:
def create
#author_id = params[:contract][:author_id]
#product = Product.new(params[:product])
...
end
Here's what I'm seeing in the log.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"...", "product"=>{"title"=>"", "handle"=>"", "description"=>"", "keywords"=>""}, "contract"=>{"author_id"=>"1"}, "commit"=>"Create Product"}
SQL (1.1ms) INSERT INTO "products" ("created_at", "description", "handle", "keywords", "title", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Mon, 08 Aug 2011 04:37:09 UTC +00:00], ["description", ""], ["handle", ""], ["keywords", ""], ["title", ""], ["updated_at", Mon, 08 Aug 2011 04:37:09 UTC +00:00]]
SQL (0.7ms) INSERT INTO "contracts" ("author_id", "created_at", "product_id", "updated_at") VALUES (?, ?, ?, ?) [["author_id", nil], ["created_at", Mon, 08 Aug 2011 04:37:09 UTC +00:00], ["product_id", 5], ["updated_at", Mon, 08 Aug 2011 04:37:09 UTC +00:00]]
Any ideas on where the problem is?
class Product < ActiveRecord::Base
has_many :authors, :through => :contracts
and
class Author < ActiveRecord::Base
has_many :products, :through => :contracts
and
class Contract < ActiveRecord::Base
belongs_to :author
belongs_to :product
end
So, jimworm's nested_attributes works, with two changes:
<%= f.fields_for :contract do |c| %>
<%= c.collection_select :author_id, Author.all, :id, :name %>
<% end %>
(assuming <%= form_for(#product) do |f| %>)
and then, in the product controller:
def new
#product = Product.new
contract = #product.contracts.build
...
Your #author_id in create_contract is the Product model, and therefore not in the same scope as your controller.
Try putting the following in your models:
class Product < ActiveRecord::Base
has_one :contract, :dependent => :destroy
has_one :author, :through => :contract
accepts_nested_attributes_for :contract
end
class Contract < ActiveRecord::Base
belongs_to :product
belongs_to :author
end
class Author < ActiveRecord::Base
has_many :contracts, :dependent => :destroy
has_many :products, :through => :contracts
end
Then in your form:
...
<%= f.fields_for :contract do |c| %>
<%= c.collection_select :author_id, Author.all, :id, :name %>
<% end %>
...
Try this and see how it goes.