Multiple t.references get nil with nested models - ruby-on-rails

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(...)

Related

Rails efficient way to find user with only one shareholder

In my method I want to check if user has only one shareholder, if it so it should return true (later on it's used in if block). Basically it should reflect something like User.find_by(id: user.id).shareholder.count < 1 because it overloads the database with unnecessary queries (I have db with ~30k users).
What I was thinking is to create queries with where so I have this one:
def one_shareholder?(shareholder)
ShareholdersUser.where(user_id: shareholder.owner_id).
where.not(shareholder_id: shareholder.id).exists?
end
But I don't know how to implement query which will be counting if this user has only one shareholder. Should I use something like find_each ?
Edit:
user has_many :shareholder
shareholder belongs_to :owner
ShareholdersUser belongs_to :user and :shareholder
Maybe this can give you an hint. I used something similar in a project where I have these models:
class Company < ApplicationRecord
has_many :permits
end
and
class Permit < ApplicationRecord
belongs_to :company
end
For fetching companies having just one permit I used:
Company.joins(:permits).group('companies.id').having('count(company_id) = 1')
Maybe you can pluck the ids and use the array to check wether the company is in the array. For example:
ids_of_companies_having_one_permit = Company.joins(:permits).group('companies.id').having('count(company_id) = 1').pluck(:id)
Then check:
if ids_of_companies_having_one_permit.include? company.id ....
This is the thread I followed: Find all records which have a count of an association greater than zero
Firstly, if you are finding user from his ID then can directly use User.find(user.id) instead of User.find_by(id: user.id)
Secondly, As you mentioned in your question that you want either true/false for your if condition.
And as per your query I think you have user has_many shareholder association implemented.
So you can directly use User.find(user.id).shareholders < 1 in your if condition like below,
if User.find(user.id).shareholders.count < 1
#do something...
end
Note: I've used the plural of the shareholder in condition because we have has_many association

Build method is not associating in Rails

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.

How would I implement a multi-type user system for Rails?

I'm creating a Rails application where users can sign up by checking a box in a form where they are either a "person" or "organization". I'm struggling to find a way to implement this into Rails. Both of these user types would have the same authorization. I have no idea if I want to use a string or a boolean as a data type in my ActiveRecord database. Also, what would I need to put in my model (User.rb) and my controller in order to validate it and implement it respectively?
There are many ways to implement this; it depends on what your needs are. Ask yourself: "Do people and organizations share the same attributes?"
AR Enum
If they do, the only thing that differentiates the two is role (or whatever you want to call it), i.e., person or organization. For that scenario, Rails 4.1 provides AR enums. This is the simplest solution, it could go something like this:
class User < ActiveRecord::Base
enum role: [ :person, :organization ] # #user.role => 'person', #user.person? => true
end
Polymorphic Association
On the other hand, if people and organizations share only some attributes, you might consider using a polymorphic association (If people and organizations share no attributes—not even role—they should be two different models). The base model should contain the attributes that both people and organizations share. The person/organization models should contain attributes specific to that model.
# common attributes
class User < ActiveRecord::Base
belongs_to :profile, polymorphic: true
def self.roles
%w(person organization)
end
end
# person-specific attributes
class PersonProfile < ActiveRecord::Base
has_one :user, as: :profile, dependent: :destroy
end
# organization-specific attributes
class OrganizationProfile < ActiveRecord::Base
has_one :user, as: :profile, dependent: :destroy
end
For user signup, you can create users#new and users#create actions. In your user signup form (perhaps app/views/users/new.html.erb), you could use a select_tag to let the user specify their role. Then, use that to determine what kind of profile to attach to your user model. For example (users#create):
def create
#user = User.new(user_params)
if role = params[:role]
# return HTTP 400
head :bad_request and return unless User.roles.include?(role)
# Assign the User's profile
#user.profile = "#{role.capitalize}Profile".constantize.new
else
# enter your own logic here
end
#user.save ? redirect_to(#user) : render(:new)
end
The handling of sessions (user signin/signout), in my opinion, should be handled in a separate SessionsController.
Add a new table to the database named user_types with fields role and id. And in users table you need to add user_type_id column. Then, in UserType model
class UserType < ActiveRecord::Base
has_many :users
end
And in User model you need to add
class User < ActiveRecord::Base
belongs_to :user_type
end
You can create UserType records in seeds, but make sure it runs everytime the database is reset, add this to seeds.rb
UserType.create!(role: "person")
UserType.create!(role: "organization")
Hope this makes sense!
If you have only two types of users (Person and Organization) as indicated in your question, you could just have a Person model, and add a bool field is_organization to it. For more details, See how devise, a popular authentication gem, handles this here (this approach is option 2 on the linked page, you can also check out option 1, which is to create an entire new model).

Rails: Association works with User but not current_user

I'm having a strange issue.
I have a number of models and associations that work perfectly together but when I try to introduce the current_user I get:
ActiveRecord::ConfigurationError at /dashboard
Message Association named 'game' was not found; perhaps you misspelled it?
Here's some code that works:
Controller:
def index
users = current_user.followed_users.collect { |user| user.id }
#userupdates = Userupdate.for_users(users, params[:page])
end
View:
<% #userupdates.each do |userupdate| %>
Things and stuff
<% end %>
But when I try to make the page display content from followed_users AND the current_user like so..
def index
users = current_user.followed_users.collect { |user| user.id }
users.push(current_user.id)
#userupdates = Userupdate.for_users(users, params[:page])
end
...I get the error above.
Some of the relavent model code:
class Userupdate < ActiveRecord::Base
belongs_to :user
belongs_to :game
class User < ActiveRecord::Base
has_many :userupdates
class Game < ActiveRecord::Base
has_many :userupdates
ActiveRecord::ConfigurationError is explained as below in rails api.
Raised when association is being configured improperly or user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
So I thought the problem is your association configured. You may check again the association, like whether model Userupdate has user_id and game_id.
And the current_user issue, maybe you should check your query sql to see whether your includes works. If works, it should do the outer join between userupdates and users, userupdates and games, and you'll see loading users and games after loading userupdates in log. And current_user maybe the only user who has the userupdates whose belonging game exists.
All my opinions, hope this can help.

Rails: create on has_one association

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

Resources