I am using (have to) Rails 3.2.12 and ruby 1.9.3 with mongoid as orm
Maybe my question doesn't make sense but let me ask
I want to know the ID or any other attribute of the unsaved document which is not being validated.
Like in the following screenshot I want to grab the ID of the second document whose one attribute is invalid. This page was rendered because server validation didn't go through.
Though it's not entirely clear from your question what is causing validation to fail, the text "can't be blank" is usually coming from ActiveModel::Errors.
Whenever validation is failed on the model and the page is re-rendered, it passes a hash that looks like this (example for a class Item that fails :create because it's missing a name):
#<ActiveModel::Errors:0x00007fe63fcff710
#base=#<Item id: nil, name: nil, user_id: 15, created_at: nil, updated_at: nil>,
#messages={:name=>["can't be blank"]},
#details={:name=>[{:error=>:blank}]}>
Inside that hash you will have lots of useful information (including the "can't be blank error" that is showing in your example). You can grab those errors in a number of different ways, but often you do it with an instance variable in the controller. For example:
class WhateverController < ApplicationController
def create
#thing = Thing.new(thing_params)
if #thing.save
...do success stuff...
else
flash[:error] = #thing.errors.full_messages
render :new
end
end
end
Call Byebug (or similiar) at the top of the else statement and then you can have a look in the console what is in #thing.errors
If the record is not being created, you won't have an ID in the hash (because it hasn't been saved), but if it's an update action then it should appear in the errors hash.
Related
If I were to assume the models:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
end
When I'm trying to run User.first.posts.create [attributes], the model gets created, but its id is nil and is not saved in the database. Could anyone explain why? I thought this sort of behaviour was expected from #new, not from #create.
Your expectations are wrong.
Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.
The implementaton is actually dead simple:
def create(attributes = nil, &block)
if attributes.is_a?(Array)
attributes.collect { |attr| create(attr, &block) }
else
object = new(attributes, &block)
object.save
object
end
end
So for example when you do:
post = Post.create(title: 'Not a valid post')
This will always return an instance of post. If you then inspect the object you can see that its not persisted and the errors object will tell you why:
post.valid? # false
post.persisted? # false
post.errors.full_messages # ["User can't be blank."]
If you create the record off an association you're also mutating it:
user = User.first
post = user.posts.create(attributes)
user.posts.to_a.include?(post) # true
This might seem like undesirable behavior but is needed for nested attributes to work properly.
Use create! (which raises an error if the record is invalid) instead in non-interactive contexts (like seed files) where creating a record should not be expected to fail. Don't use it your controllers where is invalid input is not an exceptional event - and where its just a crutch akin to throwing a bunch of log or puts statements all over the code. Write tests that cover invalid and valid input instead.
If you really need to use a debugger like byebug or pry to step into the controller action and inspect the model instance.
Failed to replace tickets because one or more of the new records could
not be saved.
My Form model has_many tickets. A ticket cannot have a form if it has a particular flag set. When i go to test this by trying to assign a ticket to an existing form, it crashes rather than pay attention to my validations.
Since its RailsAdmin, I see its just calling update_attribute (which apparently saves nested objects) before doing the error handing of checking if the Form object saves or not.
Is there any known way to get RailAdmin to gracefully catch this validation exception long enough to do its normal error handling for the Form itself failing validation? It's all part of the automatic stuff handling edits/new of an arbitrary object.
I'm using Rails 4.2.0 and RailsAdmin 1.1.1
Edit for Guilermo:
I have models on rails admin that fail to save because the nested object was invalid. Are you using the nested form field on rails admin? Could you show the rails admin configuration code on the form model and the validation? I'd be happy to help with that.
Whoever created this code used simple default things, the following is the relevant line inside the RailsAdmin config initializer.
edit do
include_all_fields
end
Observed behavior is the standard RailsAdmin field where you can search for objects, or pick from a drop down, and select one or more of them to be attached to the current object.
The nested object IS invalid (and as a result the parent object is as well). The problem is that rather than returning the parent object as invalid, the system crashes because the nested object is invalid.
The RailsAdmin code appears to call update_attribute (which throws an uncaught error), and then actually does the save! (along with crash check). I am basing this on the following code:
https://github.com/sferik/rails_admin/blob/master/lib/rails_admin/config/actions/edit.rb
Specifically:
#object.set_attributes(params[#abstract_model.param_key])
#authorization_adapter && #authorization_adapter.attributes_for(:update, #abstract_model).each do |name, value|
#object.send("#{name}=", value)
end
changes = #object.changes
if #object.save
#auditing_adapter && #auditing_adapter.update_object(#object, #abstract_model, _current_user, changes)
respond_to do |format|
format.html { redirect_to_on_success }
format.js { render json: {id: #object.id.to_s, label: #model_config.with(object: #object).object_label} }
end
else
handle_save_error :edit
end
It crashes at #object.set_attributes.
You could avoid this problem by not showing the tickets that do not have that flag on your model edit form.
You can do this like this:
edit do
field :tickets do
associated_collection_scope do
proc { |scope| scope.where(flag: false) }
end
end
end
Unfortunately this means that you'll have to specify which fields will be shown and you won't be able to use
include_all_fields
You could use the include_fields method for a nicer syntax to do that
include_fields [:tickets, :other_field, :etc]
I've subclassed the devise RegistrationsController for creating new users and added some logic before calling the superclass's 'create' method. So, something like:
class RegistrationsController < Devise::RegistrationsController
def create
super end
I can tell if the superclass encountered an error by checking resource.errors.nil?. However, I want to distinguish between different errors. For instance, I want to do something different if the error is "Email has already been taken" versus some other error return.
I can parse the string, but that seems fragile to me. What if some future upgrade of ActiveRecord or Devise changes the string? What if the string get's localized in some way I don't expect?
Is anyone handling error processing in devise more gracefully than string parsing?
you can modify devise.en.yml for any default errors
Notice that the devise_error_messages helper is just running through the errors attached to whatever you have assigned as your resource object (whatever user model you ran the install generator on).
Now, instead of just printing out the error messages in the helper, you could access their keys in a controller method, as well:
# in RegistrationsController
def create
build_resource
unless resource.valid?
if resource.errors.has_key?(:my_error_key)
# do something
end
end
end
This is just an example, of course, but hopefully it illustrates the approach you might take.
With Rails validation errors the devil is in the #details.
A typical validation error on presence looks like this:
> #user.errors
#<ActiveModel::Errors:0x007fe7e8f01234 #base=#<User id: nil,
email: "someone#else.mail", created_at: nil, updated_at: nil>,
#messages={:password=>["can't be blank"], :email=>[]},
#details={:password=>[{:error=>:blank}]}>
As you see, the failed validation is accurately described in #details of the ActiveModel Error object.
There is even a short hand method in Rails that makes it easy to test for specific validation errors:
#user.errors.added? :password, :blank
If the password is left blank, this will return true.
More about added? in the Ruby on Rails API: http://api.rubyonrails.org/classes/ActiveModel/Errors.html#method-i-added-3F
If a user tries to enter a duplicate entry into a table, they get a full page nasty error
ActiveRecord::RecordNotUnique in Admin::MerchantsController#add_alias
Mysql2::Error: Duplicate entry '2-a' for key 'merchant_id': ..
Is there a way to give a flash message instead like "This record already exists"?
This thread from railsforum can help you
I wouldn't recommend checking for the uniqueness and specifically responding to this validation rule via a flash message, this conventionally should be handled by the form's error messages instead.
Nonetheless, in the controller actions for creating and updated you could wrap the conditional block which decides where to send the user, with a check for uniqueness first:
def create
#merchant = Merchant.new params[:merchant]
if Merchant.where(:merchant_id => #merchant.id).count > 0
flash[:error] = "The merchant id #{#merchant.id} already exists"
render :create # amend this to :update when applying to the update action
else
# your normal generated save and render block
end
end
This isn't the cleanest way of achieving your goal, but I think it'll be easiest to understand.
Would really recommend the model validations and form error messages instead, which if you are usung the generated scaffolding, all you need to do is add a model validation and the form throw out the error messages to the user for you:
# app/models/merchant.rb
class Merchant < ActiveRecord::Base
validates_uniqueness_of :merchant_id
end
My validations were working for a while, or so I thought. Now I come back to them after a while doing something else and I am getting the error above. I think it means I am creating a nil object with the .new method but I am just lost. It seemed to be a problem with the creation of a new object by the controller because even if I # out the validation the next validation in the tree on another attribute of #thing throws up the same error. However even if I create the object in the console and test it's there the .save method throws up the error still - undefined method 'user_id' for nil:NilClass
ThingsController:
def create
#thing = Thing.new(params[:thing])
#thing.user_id = #currentuser_id
if #thing.save
flash[:notice] = "Successfully created thing."
redirect_to #thing
else
#flash[:notice] = "Your thing did not get created."
render 'otherthings/show'
end
end
Thing.rb
validate :user_valid
def user_valid
errors.add("you must be logged in to add a thing") unless #thing.user_id?
end
I'm a bit of a ruby on rails noob (i.e. <8weeks) and this is my first stackoverflow question, so go easy on me if this is stupidly obvious. I've tried params["thing"] too, as that worked in the console after manually creating params to match the log files (whereas params [:thing] didn't) but that doesn't change anything in terms of the error message I get.
When you are calling unless #thing.user_id?, it's flipping out because there's no #thing (it doesn't get passed from your controller to your model as an instance variable).
I don't remember how exactly it works, but I'm pretty sure that when calling validate :user_valid, it will pass along the record to be validated for you. If that's indeed the case you could try:
def user_valid
errors.add("you must be logged in to add a thing") unless user_id?
end