How to Validate a Specific Attribute on an Activerecord Without Instantiating an Object First

Is there a way to validate a specific attribute on an ActiveRecord without instantiating an object first?

Since validations operate on instances (and they use the errors attribute of an instance as a container for error messages), you can't use them without having the object instantiated. Having said that, you can hide this needed behaviour into a class method:

class User < ActiveRecord::Base
def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
unless mock.valid?
return mock.errors.has_key?(attr)
end
true
end
end

Now, you can call

User.valid_attribute?(:login, "login value")

just as you intended.

(Ideally, you'd include that class method directly into the ActiveRecord::Base so it would be available to every model.)

Perform only validation of record (without creating it)

Sure, just call valid? on the object:

user = User.new
user.valid? #=> false
user.errors #=> 'name missing...'

validate and update single attribute rails

You could validate the attribute by hand and use update_attribute, that skips validation. If you add this to your User:

def self.valid_attribute?(attr, value)
mock = self.new(attr => value)
if mock.valid?
true
else
!mock.errors.has_key?(attr)
end
end

And then update the attribute thusly:

if(!User.valid_attribute?('avatar', params[:user][:avatar])
# Complain or whatever.
end
@user.update_attribute('avatar', params[:user][:avatar])

You should get your single attribute updated while only (manually) validating that attribute.

If you look at how Milan Novota's valid_attribute? works, you'll see that it performs the validations and then checks to see if the specific attr had issues; it doesn't matter if any of the other validations failed as valid_attribute? only looks at the validation failures for the attribute that you're interested in.

If you're going to be doing a lot of this stuff then you could add a method to User:

def update_just_this_one(attr, value)
raise "Bad #{attr}" if(!User.valid_attribute?(attr, value))
self.update_attribute(attr, value)
end

and use that to update your single attribute.

How to skip validations while creating a object in activerecord

a = Article.new(title: "sfsfsd")
a.save(validate: false)

Note that save also has the ability to skip validations if passed
validate: false as an argument. This technique should be used with
caution.

http://guides.rubyonrails.org/active_record_validations.html#skipping-validations

How to create validations for initialized variables?

Rails does the validations only if you call valid? or save on the model. So if you want it to accept only values for your value when calling those methods, do a custom validation:

class MyClass < ActiveRecord::Base

validate :value_string

#further code

Setup your value validation like that under the protected scope

protected

def value_string
self.errors[:base] << 'Please assign a string to value' unless @value.match(/^\w+$/)
end

These validations won't be called without calling valid?or save, like I said before.
If you want value not to be assigned with another value than a word-like-value at any time, there's no other way than to prevent it on initialization:

def initialize(attributes = nil, options = {})
attr_value = attributes.delete(:value) if attributes
@value = attr_value if attr_value && attr_value.match(/^\w+$/)

super
end

EDIT

I would not recommend raising an ActiveRecord validation error when assigning not accepted values on initialization. Try to raise your own custom error based on ArgumentError

Outside your class

YourError = Class.new(ArgumentError)

Inside your Class

def initialize(attributes = nil, options = {})
attr_value = attributes.delete(:value) if attributes
if attr_value && attr_value.match(/^\w+$/)
@value = attr_value
elsif attr_value
raise YourError.new('Value only accepts words')
end

super
end

and then test it like this

describe Myclass do
it 'should raise an error if value is assigned with something else than a word' do
lambda{ MyClass.new(:value => 1111)}.should raise_error(YourError)
end
it 'should assign the value for words' do
MyClass.new(:value => 'word').value.should == 'word'
end
end

Instantiating an associated model input is available to populate the model attributes (has_one)

Why Credentials belongs to controller? It should belongs to user, and if you use has_one association, you should use singular noun of credential:

Model

class Credential < ActiveRecord::Base
belongs_to user
...
class User < Active ::Base
has_one :credential, :dependent => :destroy

Controller

In your controller, if you want create new object for form, you only need:

def new
@cred = current_user.build_credential
end

def create
@cred = current_user.build_credential(params[:cred])
if @cred.save
# what you do when creating object success
else
# what you do when creating object failed
end
end

View

In your new.html.erb in app/views/credentials/ folder, create your form:

<%= form_for @cred do |f| %>
...
<% end %>

To link to new.html.erb page, in your view create a link_to:

<%= link_to 'New Credential', new_credential_path %>

Routes

If you want to use RESTful route, In your routes.rb, add resources for credential you don't have:

resources :credentials

Is there a validation I can use for a specific attribute on all associated records?

Most likely the best way would be to use after_add callback (an example here), which would set to false all your existing accepted records via update_all and the latest answer with accepted set to true. It all depends on your logic.

You can also employ some other callbacks such as before_save, before_update and such with the similar functionality depending on your application specifics.

It is not quit a validation, but it will effectively maintain the required state of your model. Besides, the purpose of the validations to warn you when something is not valid, but I guess you want to save your object without such failures, and just enforce a single accepted answer.

Let me know in case you want to stop adding answers after the first one was accepted. In this case it would require a different functionality.

Validate Attribute without saving

You could try checking for specific errors related to the username, in addition to running all validations (you need to in order to get the error messages).

@user = User.new(params[:user])
if @user.invalid? && @user.errors[:username].any?
# yay!
else
# nope
end

You can run that without persisting your user to the database, since none of the methods used (including #new and #valid?) actually save the object.



Related Topics



Leave a reply



Submit