After a user signs up to my Rails 3.2.3 app which uses Devise, an after_create callback is triggered which attempts to create an Organisation record.
If this create fails validation, how can I get the error messages to display? Should I override the devise controller? Thing is, the errors are related to the creation of an Organisation, not a User (and I have a pretty vanilla devise User model set-up).
I'm doing something like this in the User model:
# after_create callback method
...
unless new_org_user.save
errors.add("Warning", "Organisation is invalid" )
end
...
Since you are firing the event in the after_create callback the record is already successfully created so no flash message will apear.
Why dont you try to move it to relation association instead and require validation of child like
class User < ActiveRecord::Base
has_one :organization
accepts_nested_attributes_for :organization
end
class Organization < ActiveRecord::Base
belongs_to :user
end
This way if organization is not valid it will fail the validation
Related
If I have two models, a User and a Post model where they are related with a has-many association (a user has many posts), can I create a validation that that triggers whenever either the User or its associated posts attributes gets changed?
i.e. We want a user to have a validation that is triggered whenever a user attribute gets updated or when one of its posts gets updated/created.
You can use validates_associated to trigger the associations on an associated model:
class User < ApplicationRecord
validates :name, presence: true
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
validates_associated :user
end
user = User.create!(name: 'Max')
post = user.posts.new
user.name = ''
post.save! # will trigger a validation error
can I create a validation that that triggers whenever either the User
or its associated posts attributes gets changed?
This is not how validations work. Validations are fired when you call .valid?, .save/save! or .update/.update! on the model. This sounds more like a normal callback or an assocation callback or a X&Y problem.
Yes, you can! You got some special functions related to lifecycle of your models.
Check here!
before_create do
** whatever you want **
end
I have these 2 models as follow
class Application < ActiveRecord::Base
has_many :commitments, class_name: "Commitment", \
:source => :application, dependent: :destroy
accepts_nested_attributes_for :commitments
after_create: update_case_code
end
class Commitment < ActiveRecord::Base
belongs_to :application
after_create: send_notification
def send_notification
ap self.application.case_code
end
end
class ApplicationsController < ApplicationController
def create
#application = Application.new(params)
#application.save
end
end
In my application_controller whenever i create a new Application record,a new record is also created in the Commitment and it tries to get the case_code from the application record but the after_create method of the application model hasnt been executed yet.
Is there any way to optimize this code so that it works properly?
Probably there is. Probably you can also use another callback on the application model which happens before, there are plenty of them. See Active Record Callbacks
However this is exactly the case, which other people call rails callback hell
The best practice here would be just creating a form object, which creates the data in the order you need and remove the callbacks
class ApplicationCommitmentForm
include ActiveModel::Model
attr_accessor ...
def submit
a = Application.create ..
a.update_case_code
a.commitments.create ...
end
end
See ActiveModel Form Objects
Btw you could also wrap the submit code into a transactions ensuring that either all records are created or in case of any errors nothing at all.
I simply want to create another record when the user signs up. I think the following are the only places where Clearance touches my app, not counting the views.
class ApplicationController < ActionController::Base
include Clearance::Controller
before_action :require_login
.
.
.
end
class User < ActiveRecord::Base
include Clearance::User
has_many :received_messages, class_name: 'Message', foreign_key: :receiver_id
has_one :privilege
end
You want after_create (or perhaps before_create, or some other hook, depending on your semantics), which is provided by Rails independent of Clearance. It lets you declare a method to run after your User record is created, and the method can create other objects that you want to exist.
class User < ActiveRecord::Base
after_create :create_other_thing
private
def create_other_thing
OtherThing.create(other_thing_attributes)
end
end
Be aware than after_create runs in the same transaction as your User creation, so if there's an exception during OtherThing.create, both it and the User will be rolled back.
Check out Active Record Callbacks for full details on how ActiveRecord lifecycle hooks work.
I have associated models User and Channel in Rails 3 app. Channel is created at the moment of User creation
class User < ActiveRecord::Base
before_create do
self.channels.build
end
has_many :channels
end
class Channel < ActiveRecord::Base
belongs_to :user
validations block
...
end
Problem is that if validations for Channel will not pass User will be created at DB but Channel won't. In what callback place Channel creation to create User and Channel in one 'transaction'? Or, maybe, there is another right way?
Thanks in advance.
UPD1:
Channel autocreate on User create placed in model because in some cases objects created not invoking controllers.
You can use "accepts_nested_attributes_for"
class User < ActiveRecord::Base
has_many :channels
accepts_nested_attributes_for :channels
end
class Channel < ActiveRecord::Base
belongs_to :user
validations block
end
You think too much. This is very common case and has a convention.
Firstly at Pedro said, you need a validation of association in Channel model. This will prevent saving of channel without user_id.
Then, in controller's create action, you just make sure all params including user object is sent here for creation.
Use validates :channels, associated: true.
You should probably review your Channel validations because if it is not saving, you're doing something your app doesn't expect.
I have the following classes in a Rails 3.1.rc4
class User < ActiveRecord::Base
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :users
end
What I'd like to do is create an associated team every time a user signs up using an activerecord callback. Something like this:
# in the User class
before_create {|user| user.create_team(name: "#{self.name}'s Team") }
However this doesn't seem to work properly. When I go to the rails console to check it, I can create a user and type user.team and I get a team as expected. However, if I do user.reload and user.team again, I get nil.
How do I get the user to properly associate with the team?
Turns out 3.1.rc4 actually has a bug which prevents user.create_team from working properly. See the issue on the rails github. A fix has been pushed so I guess it will be fixed in the next RC.
You need to do this with an after save because the id is not set until the user is saved. Rails won't let this record save because the associated record, the user model, has not been saved and does not have an id.
So you should do this:
class User < ActiveRecord::Base
belongs_to :team
after_create add_team_to_user
def add_team_to_user
Team.create({:user => self, :name => "#{name}'s Team"})
end
end
Or to prevent mass-asignment:
def add_team_to_user
self.team.create({:user => self, :name => "#{name}'s Team"})
end