Rails Mongodb can't save record - ruby-on-rails

I'm using Mongodb in my Rails app. I have 2 models, which are Account and User. Account has many users and users belongs to account.
Account model
has_many :users, :inverse_of => :account, :dependent => :destroy
validates :organization_name, presence: true, uniqueness: true
User model
belongs_to :account, :inverse_of => :users
validates :account, :presence => false
validates :email, presence: true
has_secure_password
validates :password, length: { minimum: 6 }, allow_nil: true
def User.new_token
SecureRandom.urlsafe_base64
end
def self.create_with_password(attr={})
generated_password = attr[:email] + User.new_token
self.create!(attr.merge(password: generated_password, password_confirmation: generated_password))
end
User controller
def new
#user = User.find_by(params[:id])
#user = #current_user.account.users.new
end
def create
#user = User.find_by(params[:id])
#user = #current_user.account.users.create_with_password(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
format.js
else
format.html { render 'new' }
format.json { render json: #user.errors, status: :unprocessable_entity }
format.js { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
def user_params
params.require(:user).permit(:id, :email, :password, :password_confirmation, :owner, :admin)
end
I can successfully sign up an account with a user. But when I tried to add a new user, the user record can't be saved. I assigned a password for users when creating a new user.
The error messages:
message: Validation of User failed. summary: The following errors were found: Account can't be blank resolution: Try persisting the document with valid data or remove the validations.
If I removed the self.create_with_password and manually type in the password in the form, it works. So i guess the error must be in the self create password, it seems like doesn't save the record. By the way I'm using Rails 5.0. Any idea to solve this?

Hey #ternggio Welcome to community.
Account can't be blank.
This error appear due to belongs_to statement in user.rb.
In rails > 5 belongs_to is by default required, You can set as optional with marking optional: true.
belongs_to :account, :inverse_of => :users, optional: true
and remove below line.
validates :account, :presence => false
Hope, this will help you.

Related

Devise Validations with Associations

I have a User model generated using Devise. This User model has 2 types (Buyer or Seller) created using Polymorphic Associations.
class Buyer < ActiveRecord::Base
has_one :user, as: :owner, dependent: :destroy
accepts_nested_attributes_for :user
validates_presence_of :user
validates_associated :user
class User < ActiveRecord::Base
after_commit :transaction_success
after_rollback :transaction_failed
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :owner, polymorphic: true
validates :email, presence: true
validates :first_name, presence: true
private
def transaction_success
logger.info "Transfer succeed for Account #{self.to_param}"
end
def transaction_failed
logger.warn "Transfer failed for Account #{self.to_param}"
end
end
When an User is Registered, he does it from an specific registration link (/buyer/new or /seller/new), calling the create method from the controller:
def create
#buyer = Buyer.new(buyer_params)
#user = User.new(user_params)
respond_to do |format|
begin
User.transaction do
#buyer.save
#user.owner_id = #buyer.id
#user.owner_type = "Buyer"
#user.save
end
format.html { sign_in_and_redirect #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
rescue ActiveRecord::Rollback
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
The problem is that sometimes the transaction fails but saves a Buyer without an User. Also, the Devise validations, or any validation that I include in the User Model doesn't make any difference, allowing users to be created without any email, password etc.
How can I fix the transaction and make sure that the validations work??

Saving an array of ids into a foreign key field - Rails 4

I'm having an issue with my ROR 4 application. Here's a quick background:
The application has several classes, Users, Training_Events and Mission_Notes.
A Training event can be associated with multiple users from a multi-select drop-down which builds an array of user_ids which are then saved to the Training_Event, whilst Mission_Notes can only be associated with one User and one Training_Event. Models below:
MissionNote.rb
class MissionNote < ActiveRecord::Base
belongs_to :training_event
belongs_to :user
end
User.rb
class User < ActiveRecord::Base
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
belongs_to :group
has_many :ranks
has_many :mission_notes
has_and_belongs_to_many :training_events
validates :username, presence: true
validates :email, presence: true
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :group_id, presence: true
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
end
TrainingEvent.rb
class TrainingEvent < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :mission_notes
validates :title, presence: true
validates :date, presence: true
validates :mission, presence: true
validates_format_of :video, :with => /\A(https\:\/\/)?((www\.)?youtube\.com|youtu\.?be)\/.+$\Z/, :allow_blank => true, :message => "must be a valid YouTube URL"
validates_format_of :date, :with => /\A((19|20)\d\d+)-(0[1-9]|1[012]+)-(0[1-9]|[12][0-9]|3[01])\Z/
end
What I then want it to do is on the user's profile display a list of the events that the particular user has been associated with and the mission_notes for each event. The issue I have is when I save the training event the user_id field is not saved in the database however if I do TrainingEvent.all.each{|x| x.user_ids} then I get an array of the user_ids which were saved.
Can someone help point out what I am doing wrong here and maybe help clarify while the single user_id finds nothing but user_ids returns at least an array of items.
------------------- Edit ------------------------------------
Training_Events_Controller.rb
class TrainingEventsController < ApplicationController
before_action :set_training_event, only: [:show, :edit, :update, :destroy]
before_action :admin_user, only: [:new, :edit, :update]
# GET /training_events
# GET /training_events.json
def index
#training_events = TrainingEvent.all
end
# GET /training_events/1
# GET /training_events/1.json
def show
#user_ids = #training_event.user_ids
#user = User.find(#user_ids)
#mission_notes = MissionNote.find_by(user_id: #user)
byebug
end
# GET /training_events/new
def new
#training_event = TrainingEvent.new
#user_options = User.all.map{|u| [ u.username, u.id ] }
end
# GET /training_events/1/edit
def edit
#user_options = User.all.map{|u| [ u.username, u.id ] }
end
# POST /training_events
# POST /training_events.json
def create
#training_event = TrainingEvent.new(training_event_params)
respond_to do |format|
if #training_event.save
format.html { redirect_to #training_event, notice: 'Training event was successfully created.' }
format.json { render :show, status: :created, location: #training_event }
else
format.html { render :new }
format.json { render json: #training_event.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /training_events/1
# PATCH/PUT /training_events/1.json
def update
respond_to do |format|
if #training_event.update(training_event_params)
format.html { redirect_to #training_event, notice: 'Training event was successfully updated.' }
format.json { render :show, status: :ok, location: #training_event }
else
format.html { render :edit }
format.json { render json: #training_event.errors, status: :unprocessable_entity }
end
end
end
# DELETE /training_events/1
# DELETE /training_events/1.json
def destroy
#training_event.destroy
respond_to do |format|
format.html { redirect_to training_events_url, notice: 'Training event was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_training_event
#training_event = TrainingEvent.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def training_event_params
params.require(:training_event).permit(:title, :date, :training_objective, :mission, :video, :user_ids => [])
end
end
Also as an additional can someone suggest the best way to then put this on the view in the way I described above, I realise this part is a little vague but it has sent me round the bend as I seem to get a repeating list of events and notes. Will attach view code when I have second
Since you are using HABTM, you will not have an automatic attribute of "User_ID"... You will however have "user_ids" because you have told it that there is a join table describing a many to many relationship. I suspect that when you are saving the training event you are trying to update a "user_id" attribute.. You should instead be adding the current user_id to the user_ids array attribute that represents the relation. :)
Hope this helps!
The model TrainingEvent has the attribute user_ids which i assume is an array instead of user_id.
What I suggest is receive the array of user_ids not saved but accessed using attr_accessor :user_ids then on creating Training Events, iterate through the user_ids and save each TrainingEvent with the respective user_id.
Make sure user_id is an attribute and not user_ids.

Rails behaving strange

I have rails version 3.2.13 and ruby version 1.9.3.
I have caught into very strange and interesting situation.
In my application have a model 'Product' with custom validator.
product.rb
class Product < ActiveRecord::Base
attr_accessible :description, :name, :price, :short_description, :user_id
validates :name, :short_description, presence: true
validates :price, :numericality => {:greater_than_or_equal_to => 0}
validate :uniq_name
belongs_to :user
belongs_to :original, foreign_key: :copied_from_id, class_name: 'Product'
has_many :clones, foreign_key: :copied_from_id, class_name: 'Product', dependent: :nullify
def clone?
self.original ? true : false
end
private
#Custom validator
def uniq_name
return if clone?
user_product = self.user.products.unlocked.where(:name => self.name).first
errors[:name] << "has already been taken" if user_product && !user_product.id.eql?(self.id)
end
end
In products controller's create action when I am trying to create new product
def create
#product = current_user.products.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to #product, notice: 'Product was successfully created.' }
format.json { render json: #product, status: :created, location: #product }
else
#product.errors[:image] = "Invalid file extension" if #product.errors[:image_content_type].present?
format.html { render action: "new" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Custom validator is being called when this line executed #product = current_user.products.new(params[:product]) and line # 2 of custom validator giving me error
undefined method `products' for nil:NilClass
I have inspected product object in custom validator but user_id is nil.
Why user_id is not being autoassigned?
Your help will be appreciated :)
So ... bypassing your question. Why aren't you just validating the uniqueness of name?
validates_uniqueness_of :name, :unless => :clone?
try to change .new to .build
#product = current_user.products.build(params[:product])
and be sure that you have relation in your User model
Class User < ActiveRecord::Base
has_many :products

ActiveModel::MassAssignmentSecurity::Error in UsersController#create

While applying logging concept to my book catalog display, when a user is regestering I am coming acorss this sort of error.
Can't mass-assign protected attributes: password_confirmation, password
And my code in app/model/user.rb is as follows:
class User < ActiveRecord::Base
attr_accessible :name, :password_digest
validates :name, :presence => true, :uniqueness => true
has_secure_password
end
And my code of create method in app/contollers/user_controller.rb
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to users_url, :notice => 'User #{#user.name} was successfully created.' }
format.json { render :json => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.json { render :json => #user.errors, :status => :unprocessable_entity }
end
end
end
Any help please!
If you want to assign those values in the way you're doing it, you need to add them to the attr_accessible in your model:
attr_accessible :name, :password_digest, :password, :password_confirmation
I suspect you may not want to assign both of those, so you might want to delete them from that hash first (in the controller):
user = params[:user]
user.delete(:password_confirmation)
#user = User.new(user)
You could also create a new hash containing just the values you want to use to create the new User, if you have only a few values to keep but a lot of values to ignore.
(You could also create a new "empty" User and just assign the values you want - if that makes more sense in your situation.)

mongoid can't push with HABTM relationship in rails

Im stuck on the issue a few days..Please help, thanks in advance.
This is a rails project on mongoid, there're 2 models in the project, one is User, another CustomSearchEngine:
class User
include Mongoid::Document
# Include default devise modules. Others available are:
......
# keep the CSEs
has_and_belongs_to_many :keeped_custom_search_engines, class_name: 'CustomSearchEngine', inverse_of: :consumers
# create or fork the CSEs
has_many :custom_search_engines, inverse_of: :author, dependent: :destroy
# Index
index({username: 1}, {unique: true, name: 'user_username'})
index({email: 1}, {unique: true, name: 'user_email'})
# Massive assignment for User.new
attr_accessible :email, :username, :agreement, :password, :password_confirmation
attr_accessor :agreement, :password_confirmation
validates :password_confirmation, presence: true
validates :agreement, presence: true
end
class CustomSearchEngine
include Mongoid::Document
include Mongoid::Timestamps
paginates_per 20
...
belongs_to :author, class_name: 'User', inverse_of: :custom_search_engines
has_and_belongs_to_many :consumers, class_name: 'User', inverse_of: :keeped_custom_search_engines
belongs_to :node
# Index
index({author_id: 1}, {name: 'cse_author_id'})
index({node_id: 1}, {name: 'cse_node_id'})
# validations
validates :status, presence: true, inclusion: {in: ['draft', 'publish']}
validates :author_id, presence: true
validates :node_id, presence: true
scope :recent, ->(status) { where(status: status).desc(:created_at) }
...
end
In my CustomSearchEngine controller:
current_user.keeped_custom_search_engines.push(#custom_search_engine)
Then I go to my mongodb, I see only the user document updated:
keeped_custom_search_engine_ids: ["50a208092061c770190000df"]
but the custom search engine document isn't changed:
consumer_ids: []
And I get an error: #messages={:consumers=>["is invalid"]}
Something I missed?
I think this problem is you push consumers before consumer instance save to database.
Try code like this:
def create
#team = Team.new(params[:team])
#team.own = current_user
respond_to do |format|
if #team.save
current_user.push_join_teams(#team.id)
format.html { redirect_to #team, notice: 'Team was successfully created.' }
format.json { render json: #team, status: :created, location: #team }
else
format.html { render action: "new" }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
#user.rb
def push_join_teams(tid)
return false if self.join_team_ids.include?(tid)
self.push(:join_team_ids,tid)
end

Resources