I have a User model that gets created through Devise, and after it's creation, I would like to automatically create a new Client (another model in my app). The new Client's atrribute, :user_id, should be equal to the :id of the User that was just created. I believe I need to use something like:
class Users::RegistrationsController < Devise::RegistrationsController
after_create :create_client
def create_client
Client.create(:user_id, :id) # Not sure what should go here
end
end
Is this the correct way to accomplish this? Also, if associations are important Client belongs_to :user and User has_one :client
You can add an after_create callback in User model(user.rb), check here for more information on how to create has_one associations.
class User < ActiveRecord::Base
after_save :add_client
def add_client
self.create_client(client_attribute1: value, client_attribute2: value)
end
end
Related
I am brand new to the world of rails so please forgive my ignorance with this question :)
I am currently setting up a new app that has two models:
Account
User
My seed file looks like this:
account = Account.create!(name: 'Account Name')
account.users.create!(
first_name: 'Test',
last_name: 'Test',
email: 'test#test.com,
password: 'test',
owner: true
)
And the users model is:
class User < ApplicationRecord
belongs_to :account
...
end
I have created a custom Devise registrations controller to let new users sign up:
class Users::RegistrationsController < Devise::RegistrationsController
# GET /sign_up
def new
render inertia: 'Auth/Register', props: {}
end
# POST
def create # rubocop:disable Lint/UselessMethodDefinition
super
end
def sign_up_params
params.require(:user).permit( :email, :password, :first_name, :last_name)
end
end
BUT! I need to pre-create an account for a user before I actually create the user here and this is something I have been stuck on for a few days with no luck (to the point I've considered changing the association's).
The error is:
Account must exist
Which makes sense because we don't have an account when we create a new user which is what i need to do before the user is registered.
My best guess was I needed to do something like:
# POST
def create
account = Account.create!(name: 'Test')
account.users.create!(sign_up_params)
end
to mimic the logic of the seeds file but this seems distinctly wrong to me and doesn't work.
So my question is, is it possible and how can I pre-create an Account model for a user and then associate it to the user in the registrations create method before the user is persisted to the database?
In my final production code, the goal would be to create an Account type that will be created and associated with a user on registration but for now just getting a very basic MVC up and running is the goal :)
Thank you so much in advance for any help!
First off, I think the account is supposed to belong to the user, not the opposite.
The user model User.rb should look like this:
has_one :account
And the account model Account.rb should look like this:
belongs_to :user
And to create an account once the user is created, you can simply add this line to your user model:
after_create :create_account
and the account will be created automatically.
What about using?
user = User.new(sign_up_params)
user.build_account(name: 'Test')
user.save!
The problem is the line belongs_to :account in User model.
I would suggest you break your User model in two models
User
UserDetails
You can then add belongs_to :account in UserDetails model.
This will help in independent creation of users, will not be affected by account.
Something like below.
class User < ApplicationRecord
has_one :user_detail
...
end
class UserDetails < ApplicationRecord
belongs_to :user
belongs_to :account
...
end
class Account < ApplicationRecord
has_many :user_details
...
end
I have an application where a user adds a subscription and an account is automatically created for that subscription. I also want to pass the current user to the account model as the account_manager. So far I have:
class Subscription < ActiveRecord::Base
has_one :account
after_create :create_account #after a subscription is created, automatically create an associated account
def create_account
Account.create :account_manager_id => "1" #need to modify this to get current user
end
end
class Account < ActiveRecord::Base
has_many :users
belongs_to :account_manager, :class_name => 'User', :foreign_key => 'account_manager_id'
belongs_to :subscription, :dependent => :destroy
end
This works fine for the first user obviously but any attempts I've made to pass current_user, self, params, etc fails. Also when I use the def method the subscription ID is no longer passed to the account. I tried passing the current user through the AccountController but nothing happens. In fact I can still create an account if my AccountController is completely blank. Is after_create the best way to create an associated account and how do I pass the user to the account model? Thanks!
If you are using devise, you can do this directly in the controller with the current_user helper without a callback:
# subscriptions_controller.rb
def create
...
if #subscription.save
#subscription.create_account(account_manager: current_user)
end
end
Suppose I have a User model
user.rb
class User < ActiveRecord::Base
...
end
with attributes like: name, username, access
access is an enum that tells me if the user is "staff" or "customer"
To get the name and username of the logged in user, I can do:
current_user.name
current_user.username
And suppose I have a Staff model
staff.rb
class Staff < ActiveRecord::Base
belongs_to :user
end
with attributes like: salary, phone_number
And I also have a Customer model
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
end
with attributes like: address, phone_number
I want to be able to call this on my staff's controller:
current_user.staff.salary
And this on my customer's controller:
current_user.customer.address
WHAT I TRIED SO FAR
I overwrote sessions_controller.rb
def create
super
model_name = current_user.access.capitalize.constantize
spec = model_name.where(user_id: current_user.id).take
session[:spec] = spec
end
So I'm able to access it via session[:spec], but not via current_user. Any ideas?
Well to begin with, your User model should reference the staff or customer, even if they are to stay blank
class User
has_one :staff
has_one :address
Just by doing this, you should be able to use current_user.customer.address. However...
I suggest you add some convenient methods in ApplicationController or a module that you include
def staff_signed_in?
#staff_signed_in ||= (user_signed_in? and current_user.access == :staff)
end
def current_staff
#current_staff ||= (current_user.staff if staff_logged_in?)
end
# same for customer
# Note that I use instance variables so any database queries are executed only once !
Then you can simply call
<% if customer_signed_in? %>
<h2>Logged in as customer</h2>
<p>Address : <%= current_customer.address %>
<% end %>
EDIT : about your concerns concerning database hits
You gave the example of current_user.customer.cart.products
This is indeed quite a nested association. My suggestion above already reduces it by one level (ie current_customer == current_user.customer). Then you have to go through carts to reach products... it isn't so bad in my opinion.
If you need to call that often (current_customercustomer.cart) you can override the current_customer for a given controller and eager load the resources you know you will use use.
def UserShopController < ApplicationController
# Let's assume current_customer is defined in ApplicationController like I showed above
# UserShopController always uses the customer cart, so let's load it right at the beginning
...
private
# Override with eager loading
def current_customer
#current_customer ||= (current_user.customer.includes(:cart) if customer_logged_in?)
end
add has_one :customer to your user.rb
Your user model should be like below to accessing related model.
class User < ActiveRecord::Base
has_one :customer
end
I want to know how i can use create callback like before_create or after_create for the two dependent models.
class User < ActiveRecord::Base
end
class Member < ActiveRecord::Base
end
Lets suppose i have two models called User and Member and i want to create a member whenever any user will be created and want to create user whenever any member will be created .
If i will use the after_create or before_create callback in both the models it will run as never ending loop .so how this can be done.
Just check if either of the association exists in db before creating it in after_create callback, something like this:
class User < ActiveRecord::Base
after_create :create_member
private
def create_member
unless self.member?
# create member
end
end
end
class Member < ActiveRecord::Base
after_create :create_user
private
def create_user
unless self.user?
# create user
end
end
end
If I have a user that has_many user_logins and a user_logins that belongs to user - When a user_login is created I'm using UserLogin.create(userlogin_params) and then my strong params permits the user_id column - but this alone is not saving the current users I.D to the column as it is coming out as nil.
How do I make it save the I.D?
User model:
has_many :user_logins
UserLogin model
belongs_to :user
accepts_nested_attributes_for :user
UserLoginController:
...
def create
#user_login = UserLogin.new(user_login_params)
...
end
...
def user_login_params
param.require(:user_login).permit(
:user_login_attribute1,
:user_login_attribute2,
user_attributes: [
:id,
:user_attribute1,
:user_attribute2
]
)
end
Tell me if it helps.
There are 2 issues here at hand.
First: How do you create an association with the parent record automagically there?
Second: How do you do this so your controller action isn't a giant hole waiting for a hacker to stick their nose in.
You need to start from the parent, then build the child, not start with the child and build the parent.
Consider the following:
class User < ActiveRecord::Base
has_many :logins, class_name: "UserLogin"
end
class UserLogin < ActiveRecord::Base
belongs_to :user
end
class UserLoginsController < ApplicationController
def create
if new_user_login(user_login_params).save
redirect_to :wherever
else
render :new
end
end
private
def new_user_login(attrs={})
current_user.logins.create(attrs)
end
def user_login_params
param.require(:user_login).permit(:attr_1, :attr_1)
end
Do not pass IDs into any secure params hash unless that ID is selectable by the user. If you allow an ID into secure params, a hacker can start moving records around to other objects and destroy your database integrity.
If you would like pairing help on this problem live and in person, you can check out my codementor profile at https://codementor.io/rubycasts/#reviews