简体   繁体   中英

Proper user of strong parameters, assignments, and validators in Rails 4?

What is the proper way to set strong parameters in a Rails controller and using validators with them? I've seen several examples of doing this several different ways.

This is what a typical controller def looks like for me:

# User model
class User < ActiveRecord::Base
  validates :first_name, length: { in: 2..50 }, format: { with: /[a-zA-Z0-9\s\-\']*/ }
  validates :last_name, length: { in: 2..50 }, format: { with: /[a-zA-Z0-9\s\-\']*/ }
  validates :email, presence: true, length: { 5..100 }, format: { with: /**email regex**/ }, uniqueness: { case_sensitive: false }
end

# Controller def
def valid_email
  # Strong parameters defined here?  When is an error thrown for unmatched requires/permits?
  params.require(:user)
  params[:user].permit(:email)

  # How to prevent blank params[:user][:email] from making unnecessary database call if it's blank?
  @user = User.find_by(email: params[:user][:email])

  unless @user.nil?
    # Should work because in permit whitelist?
    @user.assign_attributes(email: params[:user][:email])

    # Should not work because not in permit whitelist?
    @user.assign_attributes(first_name: params[:user][:first_name])

    # Should work just sending to private def
    @user.assign_attributes(savable_params)

    # Validate entire model
    if @user.valid?
      @user.save
    end
  end
rescue => e
  log_error(e)

  render text: "Something bad happened. Contact support.", status: :unprocessed_entity
end

private def savable_params
  params.require(:user).permit(:email)
end

How I understand it, is the params.require and params.permit allow for whitelisting of data passed to the controller/action, but doesn't keep "other" data from being seen in the params list (if it's passed).

So, leads into a few questions:

  1. Proper way of whitelisting in a similar scenario a top?
  2. How would you, say for instance, make sure params[:user][:email] isn't blank ("") before trying to do a User.find_by since we know that a blank email isn't valid anyway and do not want to make an unnecessary database call?
  3. User.valid? will validate the entire model, even though I'm interested in only the :email attribute be valid before doing a User.find_by?
  4. If you have the uniqueness validator on email, for instance, how would you keep uniqueness from hitting the database, if params[:user][:email] is blank and we're only interested in that email isn't blank, fits within a certain length, and matches the format?

Thanks again.

So this is updating an existing user?

Just do - assuming for some reason you are not sending a user id as part of your submission:

not_found if params[:user].blank? or params[:user][:email].blank?
@user = User.find_by email: params[:user][:email] or not_found
@user.update savable_params
if @user.valid?
  flash.notice = "All good"
  redirect_to @user
else
  # this has not been saved
  flash.now.alert = @user.errors.full_messages.to_sentence
  render :(whatever you call to display the form in the first place
end

and add to your application_controller:

def not_found
  raise ActionController::RoutingError.new('Not Found')
end    

to your questions:

  1. as per above. Validators run on your model, they have the logic, keep your controllers simple.
  2. I added a line for that - should work, but normally you're sending up and id of the user, so it's not something I've done. I don't bother testing it.

  3. The validation happens on the update,. it won't hit the db unless it passes, it's a few cycles, not something I've ever worried about. You could put another pre-validation in there, to save on the db roundtrip, but if your going to do that - why not put it on the html field, and save a server round trip.

  4. Put uniqueness constraint on the model and the db (the model should get it, but for race conditions, the db is your backstop). Nothing will hit the db unless it passes your validations first, so it should be rare.

oh, and I think you'll need:

params.require(:user).permit(:email, :first_name)

to get the other field to update.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM