views:

57

answers:

4

Here is the code generated by rails:

def update
  @user = User.find(params[:id])

  respond_to do |format|
    if @user.update_attributes(params[:user])
      flash[:notice] = 'User was successfully updated.'
      format.html { redirect_to(@user) }
      format.xml  { head :ok }
    else
      format.html { render :action => "edit" }
      format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
    end
  end
end      

But I don't want user to update the whole user, assume that my user have fname, lname and gender, instead of remove the gender from the view, I want to restrict that the update method ONLY accept fname and lname only, if he/she want to update the gender, I won't allow him/her to do so. How can I restrict the user to do so? thank you.

+1  A: 

Use Hash parameters of the update_attributes

@user = User.find(params[:id])
@user.update_attributes(:fname=>params[:user][:fname], :lname=>params[:user][:lname])
Salil
sorry, I would like to know, what is the @@user mean? why not @user only. thank you.
Tattat
It is wrong, it should be something like `@user.update_attributes(...)`, like you used in your example.
Veger
@Tattat:- it's just typo here sorry. but if i am not wrong `@@` variable are used to represent as `Global Variables in rails`
Salil
O... I see... I misunderstand the @@ is a new stuff. Thanks a lot.
Tattat
How is the user updated in your code? Since you are using `User.update_attributes(..)` without any id, it is impossible to find the record/user you want to update...
Veger
@Veger :- it's by mistake it should be `@user.update_attributes` thanx anyway :)
Salil
@@ is not a global variable, it is a class variable. Global variables in ruby start with a $ character.
Faisal
A: 

You can delete unwanted attributes from the param[:user] Hash:

# ...
attributes = params[:user]
gender = attributes.delete :gender
raise SomeError unless gender.blank?
if @user.update_attributes(attributes)
  # ...
end
# ...

This code removes :gender from the Hash and checks if it is filled in. If so, an exception is raised. Of course you could give a nice warning or silently ignore the fact that the gender was filled in.

Veger
+3  A: 

or add a custom @user.update_only() method, which makes it also easier to reuse in different contexts...

class User
  def update_only(attrs = {}, *limit_to)
    update_attributes(attrs.delete_if { |k,v| !limit_to.include?(k.to_sym) })
  end
end

Then just do something along the lines of

@user.update_only(params[:user], :fname, :lname)
lwe
I must say I like this idea. It would be a nice extension to ActiveRecord (or perhaps a nice extension to update_attributes, using an :except hash perhaps).
Daniel Abrahamsson
and it transparently handles attr_protected/accessible as well - feel free to pack it up as plugin, or add it to AR::Base (would make sense)
lwe
Nice idea for a general solution
bjg
+2  A: 

There are two methods in ActiveRecord that come in handy in cases like these, attr_protected and attr_accessible.

You use them like this:

class MyModel < ActiveRecord::Base
    attr_accessible :fname, :lname #Allow mass-assignment
    attr_protected :secret #Do not allow mass-assignment
end

model = MyModel.new(:fname => "Firstname", :lname => "Lastname", :secret => "haha")
puts model.fname # "Firstname"
puts model.lname # "Lastname"
puts model.secret = nil # Can not be set through mass-assignment
model.secret = "mysecret" # May only be assigned like this
puts model.secret # "mysecret"

However, if you only need this functionality at one place, then Salil's solution will work just as well.

One thing to note is that you should use attr_acessible to whitelist attributes that are OK to mass-assign, and make every other attribute protected. By doing so, you hinder mean people for updating data they are not supposed to touch.

See the docs for more info.

Daniel Abrahamsson
This is the best answer of all provided ones, no hacking and works at the model level, which is where this kind of code should be.
Faisal