#related=Book.find_all_by_related(#book.related)
if #related.count>1
#related.each do |b|
b.update_attributes(params[:book])
end
end
I'm using rails 2.3.5. for above code, first iteration works fine and show true. but for next iteration, b.update_attributes() shows false . All parameters are reaching in the iterations. Method update_attributes() have any conditions?
If your model specified attr_accessible attributes, only those attributes will be updated.
Use attr_accessible to prevent mass assignment (by users) of attributes that should not be editable by a user. Mass assignment is used in create and update methods of your standard controller.
class User < ActiveRecord::Base
attr_accessible :login, :password
end
So, doing the following will merrily return true, but will not update the status attribute.
#user.update_attributes(:status => 'active')
If you want to update the status attribute, you should assign it separately.
#user.status = 'active'
save
Related
We have user records that have an attribute called first_name. Many of these records do no have the first_name attribute filled out and thus it equals nil. We want to introduce a presence validation on this attribute. However we've come across a huge problem. If a user updates their record during any request, that request will fail. This leads to a rather abrasive error that we don't know how to handle.
One solution is to only call the validation when the user is creating a record. This works great but we want to enforce this validation when they are on the profile page and they are attempting to update their profile.
Is there a better way to handle this where we can enforce first name requirements on the update page yet still allow users to update their record without ?
Introducing validations on existing data that does not satisfy the new requirements can be problematic. This concept you're after is fundamentally migration-on-write: You've introduce a data migration that happens over time as records are written to, because the migration cannot occur without individual user input. This is one technique for migrating very large data set in zero-downtime environments, or for forcing password resets on users.
Fundamentally, you need to define the conditions in which validation must happen and find a way to test records (on create or update) for that condition. Your condition should select all new records, plus the records being updated in the context where migration is possible.
Once you've defined the condition, you can modify your validation thusly:
validates :first_name, presence: true, if: -> { condition_for_migration }
Ideally the condition should be some field or combination of fields already present in your table that correctly identifies records as ready to be migrated, but this isn't always possible.
Failing that, you could introduce a field specifically for this purpose. You might call it version_number, set all existing records to 1, and then make the default for all new records 2. Your migration might look like this:
# All existing records will have their `version_number` set to the default of 1
add_column :users, :version_number: :integer, null: false, default: 1
# Change the default to 2 for any records created after this point
change_column_default :users, :version_number, 2
You can then use version_number to tell whether validation should take place:
validates :first_name, presence: true, if: -> { version_number >= 2 }
The key is to make sure that, in the context of your profile form, you also update version_number to enable the validation of first_name:
# app/viws/users/edit.html.haml
= form_for #user do |f|
= f.hidden_field :version, value: 2
= f.input :first_name
In the absence of a real database field for this purpose, you can add a temporary one to your model, which maintains the context only for the lifetime of a particular model instance:
Add an accessor to your model, ie update_from_profile_page
Include that field in the contexts in which you want to require validation
Validate first_name during the creation of any new record
Validate first_name during any update where update_from_profile_page is true
For example:
app/models/user.rb
class User < ActiveRecord::Base
attr_accessor :update_from_profile_page
validates :first_name, presence: true, on: :create
validates :first_name, presence: true, on: :update, if: -> { update_from_profile_page }
end
app/views/user/edit.html.haml (your profile page)
= form_for #user do |f|
= f.input :first_name
app/controllers/users_controller.rb
def update
#user = User.find(params[:id])
#user = update_from_profile_page = true
#user.update(params.require(:user).permit(:first_name)
end
This is less desirable than finding a concrete business-logic-based reason for conditional validation as it involves introducing a virtual field to your model that has no functional value outside of a single specific case of a form submission.
I have a set of custom fields attached to a devise model called Entrant.
I have two forms, one for registration form (three fields) and one which sits in the account area (12 fields). Most of the custom fields area required but only within the form the sits in the account area.
How do I achieve this?
I am using rails 4.2 and ruby 2.1
You can simply specify validations on actions, that is:
validates :name, presence: true, on: :create # which won't validate presence of name on update action
If you ask where to put your custom fields, then generate devise's views and update corresponding ones with these fields.
There are several ways! You could do conditional validations, for instance
class Entrant < ActiveRecord::Base
validate :foo, if: :account_area?
def account_area?
!new_record? # Assumes that Entrant that has already been saved
# is in the account area
end
end
However, it sounds like your needs are advanced enough that you should consider making a Form Object
A form object is an object that accepts parameters, performs validations on that data, then saves a model instance.
class AccountForm
include ActiveModel::Model
include Virtus # Provides AR like attribute functionality and mass assignment
def initialize(entrant)
#entrant = entrant
end
attribute :foo, String
validates :foo, presence: true # This is only used on the account page, so no need to mess with conditional logic
def save
if valid?
persist!
true
else
false
end
end
def persist!
#entrant.update_attributes(foo: self.foo)
end
end
This is just a great example of how non-rails-specific object oriented programming can make your life easier and your app more maintainable. Make a class like above, stick it in app/forms and restart your server. Then in your controller, you'll just pass it the model
class EntrantController < ApplicationController
def update
#form = Form.new(Entrant.find(params[:id]))
#form.attributes = params[:entrant]
if #form.save
redirect_to some_path
else
render "edit"
end
end
end
By default devise only asks for a combination of email/password, you can add other fields by adding a sanitizer (see there -> Devise how to add a addtional field to the create User form?).
If you want to add other fileds to validate, you should create a secondary Entrant controller and add a specific callback to your model.
Typically:
after_update :validate_entrant_form, if: :property_changed?
I hope this will help you.
validates :name, presence: true, if: :condition_holds?
def condition_holds?
# some code here that evaluates to a boolean
end
Maybe this way help you.
Add attribute in devise model : say attr_accessor :validate_certain. In your controller action, devise model instance say #user have to update like this #user.validate_certain = true. and change your appropriate validation conditions in devise model
validates :name, presence: true, if: :validate_certain_changed?
def validate_certain_changed?
validate_certain.present?
end
When I have to do something like this I like to think of it as it validates if something in in the field but you can also take a nil value
Entrant.validates_presence_of(:foo, :allow_nil => true)
I also have this concern when using devise on customer with forms on separate pages updating different set of customer fields
I believe most of the solution works but I was looking for the simplest, easiest and foolproof way to implement the solution
Thus came this.
validates :phone, :country, :postal_code, :street_address, presence: true, allow_nil: true
The allow_nil: true instruct the model to validate the fields ONLY if it exists on the submitted form. If you want more protection, you can use extra para like :on => :update
Please, explain, how can I make records in database with several methods, changing attributes I DON'T want to be attr_accessible.
For example, in User Model:
attr_accessible :email, :password, :password_confirmation, :guest
I don't want 'admin' true or false row be placed here because of security issue
You can assign the attribute manually, for example if your model is named User you can do the following :
user = User.first
user.update_attributes(attributes_hash)
user.admin = true
user.save
attr_accessible is used only for mass assignment via update_attributes for example, but you can always assign a specific property by calling it directly like in my example above.
I think you may be asking how to change the admin attribute in your test or development environment without adding it to attr_accessable.
One way would be the toggle method. For example,
user = User.first
user.admin?
=> false
user.toggle!(:admin)
user.admin?
=> true
A couple of things about toggle to consider. The attribute must be passed as a symbol and all callbacks and validations are skipped. In other words use caution for anything outside of testing and development. So this is how you can mass-assign the admin attribute without adding it to attr_accessable.
In a project came across a requirement wherein a logged in user should be asked specific data based on his company. This specific data would be company specific, and could be mandatory or unique. This is the approach I took.
1. Defined a model to with three fields: Label (string), Mandatory(boolean), Unique(boolean).
2. The admin of the company could then enter the required fields for. e.g: Label => "Employee number", Mandatory => true, Unique => false using a simple form.
3. This data should be asked at the time of creating another record of model Redeemed Coupon for logged in user.
4. So during initialize of the Redeemed Coupon model, reopening the class, and checking the logged in user's company.
class RedeemedCoupon
def initialize(attrs = nil, options = nil)
super
if Merchant.current #this is set in the application controller as thread specific variable
coupon_custom_field = CouponCustomField.where(:merchant_id => Merchant.current).first
if coupon_custom_field and coupon_custom_field.custom_fields.size > 0
coupon_custom_field.custom_fields.each do |custom_field|
class_eval do
field custom_field.label.to_sym, :type => String
attr_accessible custom_field.label.to_sym
end
if custom_field.unique
class_eval do
index custom_field.label.to_sym
#validates_uniqueness_of custom_field.label.to_sym, :case_sensitive => false
end
end
if custom_field.mandatory
class_eval do
#validates_presence_of custom_field.label.to_sym
end
end
end
end
end
end
However the validations validates presence of and uniqueness does not work, with a failure message being given : callback not defined. this is thrown before save, when is_valid? is called object.
TO work around that
Put in custom validation
validate :custom_mandatory_unique
def custom_mandatory_unique
if Merchant.current
coupon_custom_field = CouponCustomField.where(:ira_merchant_id => Merchant.current).first
if coupon_custom_field and coupon_custom_field.custom_fields.size > 0
coupon_custom_field.custom_fields.each do |custom_field|
field_value = self.send(custom_field.label.to_sym)
self.errors.add(custom_field.label.to_sym, "cannot be blank") if !field_value.present? and custom_field.mandatory
if field_value.present? and custom_field.unique
if RedeemedCoupon.where(custom_field.label.to_sym => field_value, :merchant_id => Merchant.current).size > 0
self.errors.add(custom_field.label.to_sym, "already taken")
end
end
end
end
end
end
My questions:
1. Is this the best way of doing it.
2. Are there any gems already present (have searched, however couldnt get any)?
3. How can i use the validation helpers here instead of defining a seperate validate block?
I would define a model that stores the set of attribute mappings that correspond to a company, and an attribute model that holds its values and is associated with your coupon model. Then create a custom validation method in coupon that makes sure all of the require attributes are present based on the company id, and a method that builds them per the company association.
In Rails, what is the difference between attr_accessor and attr_accessible? From my understanding, using attr_accessor is used to create getter and setter methods for that variable, so that we can access the variable like Object.variable or Object.variable = some_value.
I read that attr_accessible makes that specific variable accessible to the outside world.
Can someone please tell me whats the difference
attr_accessor is a Ruby method that makes a getter and a setter. attr_accessible is a Rails method that allows you to pass in values to a mass assignment: new(attrs) or update_attributes(attrs).
Here's a mass assignment:
Order.new({ :type => 'Corn', :quantity => 6 })
You can imagine that the order might also have a discount code, say :price_off. If you don't tag :price_off as attr_accessible you stop malicious code from being able to do like so:
Order.new({ :type => 'Corn', :quantity => 6, :price_off => 30 })
Even if your form doesn't have a field for :price_off, if it's in your model it's available by default. This means a crafted POST could still set it. Using attr_accessible white lists those things that can be mass assigned.
Many people on this thread and on google explain very well that attr_accessible specifies a whitelist of attributes that are allowed to be updated in bulk (all the attributes of an object model together at the same time)
This is mainly (and only) to protect your application from "Mass assignment" pirate exploit.
This is explained here on the official Rails doc : Mass Assignment
attr_accessor is a ruby code to (quickly) create setter and getter methods in a Class. That's all.
Now, what is missing as an explanation is that when you create somehow a link between a (Rails) model with a database table, you NEVER, NEVER, NEVER need attr_accessor in your model to create setters and getters in order to be able to modify your table's records.
This is because your model inherits all methods from the ActiveRecord::Base Class, which already defines basic CRUD accessors (Create, Read, Update, Delete) for you.
This is explained on the offical doc here Rails Model and here Overwriting default accessor (scroll down to the chapter "Overwrite default accessor")
Say for instance that: we have a database table called "users" that contains three columns "firstname", "lastname" and "role" :
SQL instructions :
CREATE TABLE users (
firstname string,
lastname string
role string
);
I assumed that you set the option config.active_record.whitelist_attributes = true in your config/environment/production.rb to protect your application from Mass assignment exploit. This is explained here : Mass Assignment
Your Rails model will perfectly work with the Model here below :
class User < ActiveRecord::Base
end
However you will need to update each attribute of user separately in your controller for your form's View to work :
def update
#user = User.find_by_id(params[:id])
#user.firstname = params[:user][:firstname]
#user.lastname = params[:user][:lastname]
if #user.save
# Use of I18 internationalization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
Now to ease your life, you don't want to make a complicated controller for your User model.
So you will use the attr_accessible special method in your Class model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
end
So you can use the "highway" (mass assignment) to update :
def update
#user = User.find_by_id(params[:id])
if #user.update_attributes(params[:user])
# Use of I18 internationlization t method for the flash message
flash[:success] = t('activerecord.successful.messages.updated', :model => User.model_name.human)
end
respond_with(#user)
end
You didn't add the "role" attributes to the attr_accessible list because you don't let your users set their role by themselves (like admin). You do this yourself on another special admin View.
Though your user view doesn't show a "role" field, a pirate could easily send a HTTP POST request that include "role" in the params hash. The missing "role" attribute on the attr_accessible is to protect your application from that.
You can still modify your user.role attribute on its own like below, but not with all attributes together.
#user.role = DEFAULT_ROLE
Why the hell would you use the attr_accessor?
Well, this would be in the case that your user-form shows a field that doesn't exist in your users table as a column.
For instance, say your user view shows a "please-tell-the-admin-that-I'm-in-here" field.
You don't want to store this info in your table. You just want that Rails send you an e-mail warning you that one "crazy" ;-) user has subscribed.
To be able to make use of this info you need to store it temporarily somewhere.
What more easy than recover it in a user.peekaboo attribute ?
So you add this field to your model :
class User < ActiveRecord::Base
attr_accessible :firstname, :lastname
attr_accessor :peekaboo
end
So you will be able to make an educated use of the user.peekaboo attribute somewhere in your controller to send an e-mail or do whatever you want.
ActiveRecord will not save the "peekaboo" attribute in your table when you do a user.save because she don't see any column matching this name in her model.
attr_accessor is a Ruby method that gives you setter and getter methods to an instance variable of the same name. So it is equivalent to
class MyModel
def my_variable
#my_variable
end
def my_variable=(value)
#my_variable = value
end
end
attr_accessible is a Rails method that determines what variables can be set in a mass assignment.
When you submit a form, and you have something like MyModel.new params[:my_model] then you want to have a little bit more control, so that people can't submit things that you don't want them to.
You might do attr_accessible :email so that when someone updates their account, they can change their email address. But you wouldn't do attr_accessible :email, :salary because then a person could set their salary through a form submission. In other words, they could hack their way to a raise.
That kind of information needs to be explicitly handled. Just removing it from the form isn't enough. Someone could go in with firebug and add the element into the form to submit a salary field. They could use the built in curl to submit a new salary to the controller update method, they could create a script that submits a post with that information.
So attr_accessor is about creating methods to store variables, and attr_accessible is about the security of mass assignments.
attr_accessor is ruby code and is used when you do not have a column in your database, but still want to show a field in your forms. The only way to allow this is to attr_accessor :fieldname and you can use this field in your View, or model, if you wanted, but mostly in your View.
Let's consider the following example
class Address
attr_reader :street
attr_writer :street
def initialize
#street = ""
end
end
Here we have used attr_reader (readable attribute) and attr_writer (writable attribute) for accessing purpose. But we can achieve the same functionality using attr_accessor. In short, attr_accessor provides access to both getter and setter methods.
So modified code is as below
class Address
attr_accessor :street
def initialize
#street = ""
end
end
attr_accessible allows you to list all the columns you want to allow Mass Assignment. The opposite of this is attr_protected which means this field I do NOT want anyone to be allowed to Mass Assign to. More than likely it is going to be a field in your database that you don't want anyone monkeying around with. Like a status field, or the like.
In two words:
attr_accessor is getter, setter method.
whereas attr_accessible is to say that particular attribute is accessible or not. that's it.
I wish to add we should use Strong parameter instead of attr_accessible to protect from mass asignment.
Cheers!
A quick & concise difference overview :
attr_accessor is an easy way to create read and write accessors in
your class. It is used when you do not have a column in your database,
but still want to show a field in your forms. This field is a
“virtual attribute” in a Rails model.
virtual attribute – an attribute not corresponding to a column in the database.
attr_accessible is used to identify attributes that are accessible
by your controller methods makes a property available for
mass-assignment.. It will only allow access to the attributes that you
specify, denying the rest.