I have to populate a field in my db when a new user signs-up.But this field is not to be filled by the user, instead will be populated by the application in the db. Here is what i tried:
Added the extra field(investorId) in the migration file.
Overridden the devise controller:
def create
super
if #user.save
#user.investorId = #user.id + X---> some number
#user.save
end
end
Though it is working fine, but I want to know if there are better ways of doing it since I am doing it for the first time.
Thanks,
Sachin
If you need to generate a value before you create a record, you can use the before_create or after_create callbacks on the model.
User < ActiveRecord::Base
after_create :generate_investor_id
private
def generate_investor_id
reload
investor_id = "#{self.id}something"
update_attribute(:investor_id, investor_id)
end
end
Don't override your devise controller, there is no benefit of doing this. Simply put the below logic in your after_create callback -
User < ActiveRecord::Base
after_create :create_investor_id
private
def create_investor_id
self.update_column(:investor_id, id+some_number)
end
end
Related
Recommendation from GitHub devise gem:
If you have multiple Devise models, you may want to set up a
different parameter sanitizer per model. In this case, we recommend
inheriting from Devise::ParameterSanitizer and adding your own
logic:
class User::ParameterSanitizer < Devise::ParameterSanitizer
def initialize(*)
super
permit(:sign_up, keys: [:username, :email])
end
end
And then configure your controllers to use it:
class ApplicationController < ActionController::Base
protected
def devise_parameter_sanitizer
if resource_class == User
User::ParameterSanitizer.new(User, :user, params)
else
super # Use the default one
end
end
end"
Where should I create the file user_parameter_sanitizer.rb to host the code?
The info given by the devise GitHub page is excellent, but I am new to Rails and coding in general.
I have a parent User model, and each user has_one Contact which hold's the user's contact info.
One of the contact fields is the email. For various legacy reasons and reasons outside the context of this question, I'd love to be able to call #email directly as if it were a property of User
In other words I'd like to do this -
user.email
user.email = "foo#example.com"
Instead of this -
user.contact.email
user.contact.email = "foo#example.com"
I laid out my models as follows, overriding the child's getter/setter from the parent User -
class User < ActiveRecord::Base
has_one :contact
def email
contact.email
end
def email=(value)
contact.email = value
end
end
class Contact < ActiveRecord::Base
belongs_to :user
end
You may have already noticed the problem with the setter - when the User is saved, it doesn't save the child model.
What's the most robust to work around this?
Thanks!
Ok, found the solution for anyone curious - auto-saving
ActiveRecord::AutosaveAssociation is a module that helps with exactly this - saving child associations properly when the parent is saved.
Just need to include an autosave: true. Example -
has_one :contact, autosave: true
It also takes care of destroying records marked for deletion, and a few other fancy things.
I also did a quick check, and it smartly doesn't hit the DB unless it really needs to. That is, if you update the parent only it doesn't bother saving the child association with an other SQL UPDATE statement.
Check out the documentation here.
I have 2 models, product and a photos model as follows:
product.rb
class Product < ActiveRecord::Base
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, allow_destroy: true
end
photo.rb
class Photo < ActiveRecord::Base
belongs_to :product
validates :album, presence: true
mount_uploader :image, PhotoUploader
end
I'm using carrierwave & s3 for file uploads.
I can nest the forms so that users can upload photos at the same time that they create the product. In this scenario, the photo models are saved before the product model is saved, which leads to a photo file path of something like:
uploads/photos/images/photo_id
But the file structure would make more sense if it was something like:
uploads/product/id/photos/
However, product.id is nil at the time of photo creation.
With that in mind, a few questions:
Does the file structure really matter that much or am I over thinking it?
Should I do something instead where the product is saved first with basic info and then the files are attached on a separate form?
Is there something I'm overlooking like a way to trigger carrierwave to move the files after the product model is saved?
From a UI standpoint is it bad practice to have users go thru a multi-step process (in this case for creating a product)?
My solution to have a path such as uploads/products/id/photos/ is to configure the PhotoUploader as follows:
def store_dir
"uploads/products/#{model.product_id}/images"
end
Just be careful that there are no validations that would cause product_id to be non-existant at the time the store_dir method is called.
I am creating and updating objects, my controller has:
def create
#mymodel = MyModel.create mymodel_params
authorize #mymodel
end
I need to authorize create so I have added authorize #mymodel but surely this should come first? The problem is what parameter do I give authorize?
I could do
authorize :mymodel
but it seems that this is not the way Pundit is supposed to be used inside controllers that have associated policies. What is the correct way to authorize here? Apologies if I missed it in the docs.
Wouldn't you be able to do:
def create
#mymodel = MyModel.new
authorize #mymodel
#mymodel.update_attributes(mymodel_params)
end
For pundit, you can call the model name in it without it being a instance variable or symbol.
ex. Posts
class PostPolicy < ApplicationPolicy
def create?
user.admin?
end
end
class PostsController < ApplicationController
expose(:post)
def create
authorize post
post.save
respond_with(post)
end
end
The pundit section on this application will show it in action.
The correct way to do this is like this:
def create
#mymodel = MyModel.new(mymodel_params)
authorize #mymodel
#mymodel.save
end
This way, you can use the properties set in your #mymodel instance, for example:
class MyModelPolicy
def create?
#record.user == #user
end
end
So your data is not persisted before you authorize the record and you can authorize the record based on what the data will be.
Like after_add callback for has_and_belongs_to_many association is there any call back or work around to get after_add functionality for belongs_to association.
One work around is to use after/before save call backs and dirty object functionality.
belongs_to :video
after_save :after_save_task
def after_save_task
do_stuff if video_id_changed?
end
def do_stuff
### do stuff
end
But I cannot save(true) in do_stuff since its going into an infinite loop.
Looks like the feature for adding callbacks for has_on and belongs_to is not yet added. see this thread https://github.com/rails/rails/issues/586
One dirty solution for your specific problem would be to add some dirty attribute to suggest if update has already been made.
like this
belongs_to :video
after_save :after_save_task
attr_accessor :stuff_done
def after_save_task
do_stuff if video_id_changed? && !stuff_done
end
def do_stuff
stuff_done = true
### do stuff
## Saving record here would be fine.
end
Again this is truly a hack, there might exist some better solution for this.
How about overriding the setter method?
Rails guides has an exam0le of this here
an excerpt of which states
Overriding generated methods
Association methods are generated in a module that is included into
the model class, which allows you to easily override with your own
methods and call the original generated method with super. For
example:
class Car < ActiveRecord::Base belongs_to :owner belongs_to
:old_owner
def owner=(new_owner)
self.old_owner = self.owner
super
end
end
If your model class is Project, the module is named
Project::GeneratedAssociationMethods. The GeneratedAssociationMethods
module is included in the model class immediately after the
(anonymous) generated attributes methods module, meaning an
association will override the methods for an attribute with the same
name.
I appreciate this is an old question but I stumbled on it looking for a similar solution