Hi (huge Rails newbie here), I have the following models:
class Shop < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :title, :user_id, :message => "is already being used"
end
and
class User < ActiveRecord::Base
has_one :shop, :dependent => :destroy
end
When I'm about to create a new shop, I get the following error:
private method `create' called for nil:NilClass
This is my controller:
#user = current_user
#shop = #user.shop.create(params[:shop])
I've tried different variations by reading guides and tutorials here and there, but I'm more confused than before and can't get it to work. Any help would be greatly appreciated.
A more concise way to do this is with:
#user.create_shop(params[:shop])
See methods added by has_one in the Ruby on Rails guides.
First of all, here is how to do what you want:
#user = current_user
#shop = Shop.create(params[:shop])
#user.shop = #shop
Now here's why your version did not work:
You probably thought that this might work because if User had a has_many relation to Shop, #user.shops.create(params[:shop]) would work. However there is a big difference between has_many relations and has_one relations:
With a has_many relation, shops returns an ActiveRecord collection object, which has methods that you can use to add and remove shops to/from a user. One of those methods is create, which creates a new shop and adds it to the user.
With a has_one relation, you don't get back such a collection object, but simply the Shop object that belongs to the user - or nil if the user doesn't have a shop yet. Since neither Shop objects nor nil have a create method, you can't use create this way with has_one relations.
Two more ways if you want save instead of create:
shop = #user.build_shop
shop.save
shop = Show.new
shop.user = #user
shop.save
Just to add to above answers -
#user.create_shop(params[:shop])
Above syntax creates new record but it subsequently deletes similar existing record.
Alternatively, if you do not want to trigger delete callback
Shop.create(user_id: user.id, title: 'Some unique title')
This thread might be helpful. Click here
Related
I'm still newbie in Rails, but got confused with the initialization of a HABTM association. Reading its documentation, it says
When initializing a new has_one or belongs_to association you must use the build_ prefix to build the association, rather than the association.build method that would be used for has_many or has_and_belongs_to_many associations.
So, basically, let's suppose we have two models:
class User < ApplicationRecord
has_and_belongs_to_many :organizations
end
class Organization < ApplicationRecord
has_and_belongs_to_many :users
end
Inside organization_controller, since I'm using Devise, my create method should have something like this:
#organization = current_user.organizations.build(organization_params)
#organization.save
However, it is not working. With byebug, I checked that for the current_user.organizations, the new organization was there, but, if I call #organization.users, there's an empty array. Looks like it's required to run current_user.save as well, is it correct? I was able to associate both models with this code:
#organization = Organization.new(organization_params)
#organization.users << current_user
#organization.save
You should highly consider using has_many, :through as that's the preferred way to do these kinds of relationships now in Rails.
having said that if you want to use has_and_belongs_to_many associations yes its get stored in join table on main object save.
I am using Devise for users.
User.rb
belongs_to shop
has_many tasks
Show.rb
has_many users
has_many tasks
Task.rb
belongs_to user
belongs_to shop
when I create a new task:
current_user.tasks.create(...)
the shop_id gets the value of nil, when I need to be the same shop_id as the user.
when I create a new task
current_user.shop.tasks.create(...)
I get the user_id as nil but gets the right value for the shop_id.
what am I am missing?
thanks in advance.
When
current_user.tasks.create(...)
is run rails association would not know that it has to populated shop_id unless you explicitly send it
current_user.tasks.create(shop_id: current_user.shop.id)
Same the other way around. You can use a better modeling for this case with polymorphic association between user, shops and tasks. More details and example can be found here.
See
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Do not think this is related to devise.
In current_user.shop.tasks.create(...) you're calling create directly on the tasks collection for a singular shop. This is effectively equivalent to:
Shop.find_by(user_id: current_user.id).tasks.create(...)
Shops can possibly have more than one user, so there's nothing explicit in that statement that the newly created task should belong to current_user.
I think the simplest solution is to create the task by itself, explicitly setting both foreign keys:
Task.create(shop_id: current_user.shop_id, user_id: current_user.id)
Though you'd have to reload your user and shop references to pick up the newly associated task.
If you want something more automatic, consider using an association callback on has_many :tasks within user where shop_id for a Task is set from the user's shop_id:
class user < ActiveRecord::Base
belongs_to :shop
has_many :tasks, before_add: :assign_to_shop
def assign_to_shop(task)
task.shop = self.shop
...
end
end
Devise current_user method returns same object of user.
# simple example
def sign_in
session[:current_user] = User.find_by_email(params[:email])
end
def current_user
session[:current_user]
end
If user signed in, then current_user method should work, as like in right below.
#1
current_user.tasks.create(...)
#2 you can also like this
t = Task.new(...)
t.user_id = current_user.id
t.save
You can play in rails console, easy to understand it.
current_user = User.first
current_user.tasks.create(...)
I have two models.
user.rb
class User < ActiveRecord::Base
has_many :trial_subscriptions
attr_accessible :trial_subscriptions_attributes
end
in my trial_subscription.rb
the model is
class TrialSubscription < ManualSubscription
end
Please note that the ManualSubscription model inherits from the subscription model and this model has a
belongs_to :user
I am trying to use the 4.1.1.3 build_association(attributes = {}) from the active record guide to build the user. I need some more detailed explanation.
in my rails console I am doing the following and my reasoning for the following
#subscription = TrialSubscription.new() #creating a new object
#user = #subscription.build_user(email: 'blahblahblah#gmail.com', password:'eightcharacterslong')#building the user
#user.save #saving the user permanently
User.last #saved user shows up
but when I do the following
#a = User.last
#a.trial_subscriptions
I am getting an empty array.
When I do
Trial_Subscription.last, there is no extra record (the TrialSubscription.new ? )
I would expect the last trial_subscription record to have its
user_id #be filled in with the build user that I just created.
some explanation would be great!
these code will be going to my trial_subscriptions_controller.rb and there will be a form posting user inputted information.
Also I just noticed that I can do a
#user.trial_subscriptions.create!() #and then maybe a #user.save
but I feel like the first way should work.
Let's look at your two models first:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :trial_subscriptions
attr_accessible :trial_subscriptions_attributes
end
# app/models/trial_subscription.rb
class TrialSubscription < ManualSubscription
end
The point of discussion should be your usage of attr_accessible :trial_subscriptions_attributes, what this does is allows trial_subscriptions_attributes for mass assignment. Now the point to note here is, whichever model you define attr_accessible :association_attributes in should be the model you use to create the association.
The next point point of discussion would be around mass_assignment. You have allowed trial_subscriptions_attributes for mass assignment, but trial_subscriptions_attributes is not a member attribute of the model class User. You have trial_subscriptions but not trial_subscriptions_attributes. You could simply define a getter and a setter for trial_subscriptions_attributes, but Rails already provides accepts_nested_attributes_for helper for doing so for associations. So update your model User to add accepts_nested_attributes_for :trial_subscriptions:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :trial_subscriptions
accepts_nested_attributes_for :trial_subscriptions
attr_accessible :trial_subscriptions_attributes
end
With this setup you should be able to create user with associated trial subscriptions as:
#user = User.new(email: 'blahblahblah#gmail.com', password:'eightcharacterslong')
#subscription = #user.trial_subscriptions.build
#user.save #saving the user permanently
User.last // Shows last user
User.last.trial_subscriptions #shows last users trial subscriptions
With the approach you're taking to create users through trial subscription, you need to define accepts_nested_attributes_for and attr_accessible in trial_subscription model instead of user model.
Hope this is helpful.
Is it possible to build an object through two has_many associations? For example:
# posts_controller.rb
def create
#post = current_account.posts.build(params[:post])
#post.author = current_user # I want to compact this line into the previous one
end
I did some research and found this:
#article = current_account.posts.build(params[:post], user_id: current_user.id)
However, that did not work. In console, I kept getting user_id: nil whenever I built a new object.
Another potential solution I could not implement:
#post = current_account.post_with_user(current_user).build(params[:post])
But every implementation of post_with_user I wrote failed.
My associations are as follows:
class Discussion < ActiveRecord::Base
belongs_to :account
belongs_to :author, class_name: 'User', foreign_key: 'user_id', inverse_of: :discussions
end
class User < ActiveRecord::Base
belongs_to :account
has_many :discussions, inverse_of: :author
end
class Account < ActiveRecord::Base
has_many :users, inverse_of: :account
has_many :discussions
end
The thing your code shows you trying to do, you should be able to do. It should look something like this:
#article = current_account.posts.build(params[:post])
Because you're building off of the list of the current account's posts, you don't have to pass the current account's ID. (I'm not sure if your current_user is the same as your current_account, you may wish to clarify this).
To compact your post creation into one line, you can do one of two things.
Turn the relationship between a user/author and a post into a two-way relationship. Check out the documentation http://guides.rubyonrails.org/association_basics.html where an order belongs_to a customer, and a customer has_many orders. You can customize the name of the relationship so that a post has an "author" instead of a user, by calling it "author" but then using the class_name parameter which I assume would take the value :user.
Add a after-create hook to the Post class, and set the author value to the same as the current user. I can't fill in much more detail about this without knowing anything about your user subsystem.
The params variable is just a hash, so something along these lines should work to give you a one liner:
#post = current_account.posts.build params[:post].merge({ :user_id => current_user.id })
I have Orders which has many LineItems. LineItems has_many Leads. Leads aren't associated to a User until a User purchases them. The association is setup through a HABTM relationship and a join table LeadsUsers.
Once a User purchases a lead I need to setup the association. I have a LeadsUsers model with the right HABTM code setup. In my order model I have the following:
has_many :line_items
after_save :assign_lead_to_user
def assign_lead_to_user
self.line_items.each do
leads_users = LeadsUsers.create :user_id => :user_id, :lead_id => line_item.lead.id
leads_users.save
end
end
This method fails: undefined local variable or method `line_item'. I know this means that it doesn't know what line_item I am referring to... Any ideas? Ultimately I want to be able to reference User.leads.all.
After a short look, you need to provide a block variable:
self.line_items.each do |line_item|