3

First of all, I believe there must be some people, who already asked this question before but I don't know how can I google this problem. So, if it is duplicate I am sorry.

I am working on a social media site. I have user model, which I use to register users to the site. It validates, name, email, and password when registering.

I use the same model to make users edit their informations, like username.

This is what I have in my update controller:

  def update
    # Find an existing object using form parameters
    @profile = User.find_by_id(current_user.id)
    # Update the object
    if @profile.update_attributes!(settings_profile_params)
      # If save succeeds, redirect to itself
      redirect_to request.referrer
    else
      # If save fails, redisplay the form so user can fix the problems
      render('edit')
    end
  end

  private # user_params is not an action, that is why it is private.
  def settings_profile_params
    params.require(:user).permit(:first_name, :last_name, :username, :school, :program, :website, :information)
  end

The problem is, I only want to update strong parameters that I defined there. But I am getting an exception because of password validation. I don't know why am I getting this exception. How can I set up system to update the values in strong parameter only.

Thank you.

cyonder
  • 852
  • 1
  • 15
  • 36

3 Answers3

2

You can achieve this by changing you password validation. You need to add a condition on password validation.

# Password
  validates :password,
            :presence => {:message => 'Password cannot be blank'},
            :length => {:within => 8..99, :message => 'Password length should be within 8 and 99 characters'}
            :if => Proc.new { new_record? || !password.nil? }
Muntasim
  • 6,689
  • 3
  • 46
  • 69
  • This works very well and it is clean but I've never seen an 'if' like that. What do it do? And also, why did you removed lambda and used Proc.new? – cyonder Oct 03 '15 at 03:52
  • I didn't remember this syntax. Agreed that [it is the better solution](http://guides.rubyonrails.org/active_record_validations.html#combining-validation-conditions). – New Alexandria Oct 03 '15 at 03:53
  • Also, I have 1 more question. As I said I get password validation error when I try updating the records. But, why am I not getting email validation error? – cyonder Oct 03 '15 at 03:55
  • Regarding lambda or Proc : http://stackoverflow.com/questions/6232099/rails-validation-if-proc-new-or-lambda – Muntasim Oct 03 '15 at 04:01
  • @Muntasim thanks for your help, and your answer fixed the problem, but I actually made a silly mistake, that's why it wasn't working. You may wanna check the answer I shared. – cyonder Oct 03 '15 at 04:09
0

By calling update_attributes you are implicitly invoking the same range of validations as an other update and save. You need to update on the specific params you're targeting (e.g. omitting :password).

Here, we can store that list of permitted keys in a variable that is reusable. Then we call update_attribute on each of those keys — doing so within a reduce that gives the same true/false for the switch to edit or display.

  def update
    # Find an existing object using form parameters
    @profile = User.find_by_id(current_user.id)
    # Update the object
    if PERMITTED_KEYS.reduce(true) {|bool, key| bool &&= @profile.update_attribute(key, @profile.send(key)) }
      # If save succeeds, redirect to itself
      redirect_to request.referrer
    else
      # If save fails, redisplay the form so user can fix the problems
      render('edit')
    end
  end

  private 

  PERMITTED_KEYS = [:first_name, :last_name, :username, :school, :program, :website, :information]

  # user_params is not an action, that is why it is private.
  def settings_profile_params
    params.require(:user).permit(PERMITTED_KEYS)
  end

Having not used strong_parameters gem before, I think this would be more idiomatic to the use of the gem:

  def update
    # Find an existing object using form parameters
    @profile = User.find_by_id(current_user.id)
    # Update the object
    if settings_profile_params.keys.reduce(true) {|bool, key| bool &&= @profile.update_attribute(key, @profile.send(key)) }
      # If save succeeds, redirect to itself
      redirect_to request.referrer
    else
      # If save fails, redisplay the form so user can fix the problems
      render('edit')
    end
  end

  private 

  # user_params is not an action, that is why it is private.
  def settings_profile_params
    params.require(:user).permit(
      :first_name, :last_name, :username, 
      :school, :program, 
      :website, :information
    )
  end

Though, I still think this is a duplicate question, since it regard how to update model data without all of the defined validation. I've answered in case the update_attributes loop is felt to be a sufficiently unique solution to warrant non-duplication.

New Alexandria
  • 6,951
  • 4
  • 57
  • 77
0

Okay, now I found the problem. First of all, @Muntasim figured out a way to solve this problem. But I actually don't need to use this solution, because there is another easy way to fix this.

In this situation, when I let users to update their profiles, rails should not validate my password or any other column in user model, if I don't ask it to. But why was it validating? Because I have validates :password in user model. Instead it has to be validates :digest_password. Because I am using bcrypt.

I don't know why :password was working fine when I register even though I used bcrypt.

cyonder
  • 852
  • 1
  • 15
  • 36
  • Your user model does not show the use of bcrypt, btw? – Muntasim Oct 03 '15 at 04:23
  • No, it does. Here: has_secure_password validations: false – cyonder Oct 03 '15 at 04:25
  • 1
    ah, OK, But if you want to use `validates :digest_password.` still you would need the condition, right? – Muntasim Oct 03 '15 at 04:38
  • No, you don't need the condition. The thing is, as you know we put all the validation logic into model. And when you need to edit a value you gotta use strong parameters for mass assignment. And, in this strong parameters, whatever you define, model is going to validate it automatically. So, in my example, user his first name and because of that my model checks it. But the problem was password. I didn't put password in strong parameters but model was still validating it, which doesn't make sense at all. So, the problem was wrong column name in model. – cyonder Oct 03 '15 at 04:49
  • @Muntasim Well, I am gonna go crazy now. Everything was working just fine after I used digest_password. But now, after 30min it started validating password_digest. I guess I need your condition. I don't know.. I am just pissed off. – cyonder Oct 03 '15 at 05:03
  • Though I have not use the `strong_parameters` gem before, I do not suspect that it is as universally-considered a best practice as some of your wording implies. I think you should consider that the gym is potentially changing the behavior of how the ORM operates, when validating. `:digest_password` is just as much a part of the user model as any other field. – New Alexandria Oct 03 '15 at 17:37
  • Well, strong parameters isn't a gem. It is rails 4 feature. But I will try using attr_accessible, maybe that'll fix my problem. – cyonder Oct 03 '15 at 17:42
  • @NewAlexandria I opened up another question this morning, explained this situation better, you may wanna check. http://stackoverflow.com/questions/32924306/validating-the-values-that-are-in-form-only – cyonder Oct 03 '15 at 17:45