I have this model:
class User < ActiveRecord::Base
attr_accessible :subscription_process
def self.prepare_user
user = User.new
user.subscription_process = true
user.save
end
end
Inn the email that is send to the user - I use devise I have subscription_process that is equal to true. I want to know if subscription_process is saved somewhere?
Don't confuse attr_accessor and attr_accessible - those are two completely different things.
As for the question, the value is stored in the database.
user.subscription_process = true
user.save # here, it gets saved.
When you say obj.save then the it would be inserted in database and the values would be hold in that object. In your case when you save it, it will insert in Users table in database and the values are available in user object with id.
To understand attr_accessible and attr_accessor please go through this link:
Difference between attr_accessor and attr_accessible
Hope this helps !!!
Related
i have a special case for which i need to know the best practice.
Given a simple has_many association:
class Authentication < ActiveRecord::Base
belongs_to :user
#provider can be :password, :facebook_oauth etc
#code is the encrypted password on provider == :password
end
class User < ActiveRecord::Base
has_many :authentications
#this works
def encrypted_password=(pw)
set = false
self.authentications.each do |auth|
if auth.provider.to_sym == :password
set = true
auth.code = pw
end
end
self.authentications.build(provider: :password, code: pw) unless set
pw
end
#this only when no password-auth exist yet
def encrypted_password=(pw)
self.authentications.find_or_initialize_by(provider: :password).code = pw
end
end
and then
user = User.last
user.password="abcdefg"
user.save
While the first solution works, it loads and iterates over ALL associated Authentication objects. It was a workaround but this is a no-go.
The second solution does not work when it loads an existing Password-Authentication object. The User object does not know about the change on the Authentication object loaded with the find_or_initialize_by method. The change won't be saved...
Is there a way to register the changed Authentication object back to the User object so that it will be autosaved when called user.save?
It seems saving associating object returned with find back to parent object is impossible as of now. Refer to this issue https://github.com/rails/rails/issues/17466.
I had the same issue, and my workaround was, even though this is not you nor I wanted, to use save in the method yourself and make all the saves inside the transaction.
def encrypted_password=(pw)
self.authentications.find_or_initialize_by(provider: :password).update_attribute(code, pw)
end
Is there a way to register the changed Authentication object back to the User object so that it will be autosaved when called user.save?
If your question only consists of needing to know how to save an associated class, you can add this to your class definition:
class User < ActiveRecord::Base
has_many :authentications, autosave: true
end
The Authentication object is already referenced back to the User object via the user_id column that should be on Authentication by way of the belongs_to method. This autosave: true will save the associated object Authentication when the parent object (User) is saved.
I'm developing an app that requires the user to enter info into a form. Its has basic validation on some of the fields to check that the're not blank.
The user model has many user_entries
has_many :user_entries, dependent: :destroy
and the user_entry model belongs to user
belongs_to :user
the problem arises with the validation in the user_entry model
validates :name, :address, :email, presence: true
before adding the foreign key user_id to the user_entries table this code worked fine, I could fill out the form with no problem and add the entries to the table. But I need to capture the current user id within the user_entries table so I can trace an entry to a user.
def create
#user_entry = UserEntry.new(params[:user_entry])
#user_entry.add_comp_connections_from_entered_competition(current_entered_competition)
#user = current_user
#user_entry = #user.user_entries.build
#--etc--#
removing the validation lets me do this, but I don't want to remove it. It seems that removing the foreign key lets me validate. But I want both to work, any help would be very much appreciated, thanks
As stated by SteveTurczyn changing
#user = current_user
#user_entry = #user.user_entries.build
to just
#user_entry.user = current_user
makes the code work, like solving so many problems its a case of not over complicating things.
thanks again Steve Turczyn
For my app, I have different signup entry points that validate things differently.
So in the main signup, nothing is required except for the email and password field. In an alternative signup field, many more are required. So in the user model I have
validate_presence_of :blah, :lah, :foo, :bah, :if => :flag_detected
def flag_detected
!self.flag.nil?
end
I want to set that flag through the controller. However that flag isn't a database field. I'm just wondering if this is achievable in Rails or there is something wrong with the way that I am thinking about this? If so, what's the best way to achieve this? Thanks.
What you need is attr_accessor
class User < ActiveRecord::Base
attr_accessor :flag
attr_accessible :flag # if you have used attr_accessible or attr_protected else where and you are going to set this field during mass-assignment. If you are going to do user.flag = true in your controller's action, then no need this line
end
basically attr_accessor :flag create the user.flag and user.flag = ... methods for your model.
and attr_accessible is for mass-assignment protection.
Following up on the best practice debate:
Create a method that does what you want. I.e. save_with_additional_validation. This is much more clear and self-documenting code and works the same way. Just call this method instead of save()
It seems like you need to define setter method
class User < ActiveRecord::Base
attr_accessible :flag
def flag=(boolean)
boolean
end
end
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.
Clients have many Invoices. Invoices have a number attribute that I want to initialize by incrementing the client's previous invoice number.
For example:
#client = Client.find(1)
#client.last_invoice_number
> 14
#invoice = #client.invoices.build
#invoice.number
> 15
I want to get this functionality into my Invoice model, but I'm not sure how to. Here's what I'm imagining the code to be like:
class Invoice < ActiveRecord::Base
...
def initialize(attributes = {})
client = Client.find(attributes[:client_id])
attributes[:number] = client.last_invoice_number + 1
client.update_attributes(:last_invoice_number => client.last_invoice_number + 1)
end
end
However, attributes[:client_id] isn't set when I call #client.invoices.build.
How and when is the invoice's client_id initialized, and when can I use it to initialize the invoice's number? Can I get this logic into the model, or will I have to put it in the controller?
Generate a migration that adds invoices_number column to users table. Then in Invoice model write this:
class Invoice < ActiveRecord::Base
belongs_to :user, :counter_cache => true
...
end
This will automatically increase invoices_count attribute for user once the invoice is created.
how about this:
class Invoice < ActiveRecord::Base
...
def initialize(attributes = {})
super
self.number = self.client.invoices.size + 1 unless self.client.nil?
end
end
Here is some useful discussion on after_initialize per Jonathan R. Wallace's comment above:
http://blog.dalethatcher.com/2008/03/rails-dont-override-initialize-on.html
first of all, you don't need to use the attributes collection, you can just do self.client_id. Better yet, as long as you have a belongs_to :client in your Invoice, you could just do self.client.last_invoice_number. Lastly, you almost always want to raise an exception if an update or create fails, so get used to using update_attributes!, which is a better default choice. (if you have any questions about those points, ask, and I'll go into more detail)
Now that that is out of the way, you ran into a bit of a gotcha with ActiveRecord, initializer methods are almost never the right choice. AR gives you a bunch of methods to hook into whatever point of the lifecycle you need to. These are
after_create
after_destroy
after_save
after_update
after_validation
after_validation_on_create
after_validation_on_update
before_create
before_destroy
before_save
before_update
before_validation
before_validation_on_create
before_validation_on_update
What you probably want is to hook into before_create. Something like this
def before_create
self.number ||= self.client.last_invoice_number + 1 unless self.client
end
What that will do is it will hit up the database for your client, get the last invoice number, increment it by one, and set it as its new number, but only if you haven't already set a number (||= will assign, but only if the left side is nil), and only if you have set a client (or client_id) before the save.